xref: /openbmc/linux/arch/mips/kernel/vpe.c (revision b618336a)
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>
56e01402b1SRalf Baechle 
57e01402b1SRalf Baechle typedef void *vpe_handle;
58e01402b1SRalf Baechle 
59e01402b1SRalf Baechle #ifndef ARCH_SHF_SMALL
60e01402b1SRalf Baechle #define ARCH_SHF_SMALL 0
61e01402b1SRalf Baechle #endif
62e01402b1SRalf Baechle 
63e01402b1SRalf Baechle /* If this is set, the section belongs in the init part of the module */
64e01402b1SRalf Baechle #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
65e01402b1SRalf Baechle 
6641790e04SRalf Baechle /*
6741790e04SRalf Baechle  * The number of TCs and VPEs physically available on the core
6841790e04SRalf Baechle  */
6941790e04SRalf Baechle static int hw_tcs, hw_vpes;
70e01402b1SRalf Baechle static char module_name[] = "vpe";
71307bd284SRalf Baechle static int major;
7227a3bbafSRalf Baechle static const int minor = 1;	/* fixed for now  */
73e01402b1SRalf Baechle 
742600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
752600990eSRalf Baechle  static struct kspd_notifications kspd_events;
762600990eSRalf Baechle static int kspd_events_reqd = 0;
772600990eSRalf Baechle #endif
782600990eSRalf Baechle 
79e01402b1SRalf Baechle /* grab the likely amount of memory we will need. */
80e01402b1SRalf Baechle #ifdef CONFIG_MIPS_VPE_LOADER_TOM
81e01402b1SRalf Baechle #define P_SIZE (2 * 1024 * 1024)
82e01402b1SRalf Baechle #else
83e01402b1SRalf Baechle /* add an overhead to the max kmalloc size for non-striped symbols/etc */
84e01402b1SRalf Baechle #define P_SIZE (256 * 1024)
85e01402b1SRalf Baechle #endif
86e01402b1SRalf Baechle 
872600990eSRalf Baechle extern unsigned long physical_memsize;
882600990eSRalf Baechle 
89e01402b1SRalf Baechle #define MAX_VPES 16
902600990eSRalf Baechle #define VPE_PATH_MAX 256
91e01402b1SRalf Baechle 
92e01402b1SRalf Baechle enum vpe_state {
93e01402b1SRalf Baechle 	VPE_STATE_UNUSED = 0,
94e01402b1SRalf Baechle 	VPE_STATE_INUSE,
95e01402b1SRalf Baechle 	VPE_STATE_RUNNING
96e01402b1SRalf Baechle };
97e01402b1SRalf Baechle 
98e01402b1SRalf Baechle enum tc_state {
99e01402b1SRalf Baechle 	TC_STATE_UNUSED = 0,
100e01402b1SRalf Baechle 	TC_STATE_INUSE,
101e01402b1SRalf Baechle 	TC_STATE_RUNNING,
102e01402b1SRalf Baechle 	TC_STATE_DYNAMIC
103e01402b1SRalf Baechle };
104e01402b1SRalf Baechle 
105307bd284SRalf Baechle struct vpe {
106e01402b1SRalf Baechle 	enum vpe_state state;
107e01402b1SRalf Baechle 
108e01402b1SRalf Baechle 	/* (device) minor associated with this vpe */
109e01402b1SRalf Baechle 	int minor;
110e01402b1SRalf Baechle 
111e01402b1SRalf Baechle 	/* elfloader stuff */
112e01402b1SRalf Baechle 	void *load_addr;
113571e0bedSRalf Baechle 	unsigned long len;
114e01402b1SRalf Baechle 	char *pbuffer;
115571e0bedSRalf Baechle 	unsigned long plen;
1162600990eSRalf Baechle 	unsigned int uid, gid;
1172600990eSRalf Baechle 	char cwd[VPE_PATH_MAX];
118e01402b1SRalf Baechle 
119e01402b1SRalf Baechle 	unsigned long __start;
120e01402b1SRalf Baechle 
121e01402b1SRalf Baechle 	/* tc's associated with this vpe */
122e01402b1SRalf Baechle 	struct list_head tc;
123e01402b1SRalf Baechle 
124e01402b1SRalf Baechle 	/* The list of vpe's */
125e01402b1SRalf Baechle 	struct list_head list;
126e01402b1SRalf Baechle 
127e01402b1SRalf Baechle 	/* shared symbol address */
128e01402b1SRalf Baechle 	void *shared_ptr;
1292600990eSRalf Baechle 
1302600990eSRalf Baechle 	/* the list of who wants to know when something major happens */
1312600990eSRalf Baechle 	struct list_head notify;
13241790e04SRalf Baechle 
13341790e04SRalf Baechle 	unsigned int ntcs;
134307bd284SRalf Baechle };
135307bd284SRalf Baechle 
136307bd284SRalf Baechle struct tc {
137307bd284SRalf Baechle 	enum tc_state state;
138307bd284SRalf Baechle 	int index;
139307bd284SRalf Baechle 
14007cc0c9eSRalf Baechle 	struct vpe *pvpe;	/* parent VPE */
14107cc0c9eSRalf Baechle 	struct list_head tc;	/* The list of TC's with this VPE */
14207cc0c9eSRalf Baechle 	struct list_head list;	/* The global list of tc's */
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);
157e01402b1SRalf Baechle extern void save_gp_address(unsigned int secbase, unsigned int rel);
158e01402b1SRalf Baechle 
159e01402b1SRalf Baechle /* get the vpe associated with this minor */
160e01402b1SRalf Baechle struct vpe *get_vpe(int minor)
161e01402b1SRalf Baechle {
162e01402b1SRalf Baechle 	struct vpe *v;
163e01402b1SRalf Baechle 
1642600990eSRalf Baechle 	if (!cpu_has_mipsmt)
1652600990eSRalf Baechle 		return NULL;
1662600990eSRalf Baechle 
167e01402b1SRalf Baechle 	list_for_each_entry(v, &vpecontrol.vpe_list, list) {
168e01402b1SRalf Baechle 		if (v->minor == minor)
169e01402b1SRalf Baechle 			return v;
170e01402b1SRalf Baechle 	}
171e01402b1SRalf Baechle 
172e01402b1SRalf Baechle 	return NULL;
173e01402b1SRalf Baechle }
174e01402b1SRalf Baechle 
175e01402b1SRalf Baechle /* get the vpe associated with this minor */
176e01402b1SRalf Baechle struct tc *get_tc(int index)
177e01402b1SRalf Baechle {
178e01402b1SRalf Baechle 	struct tc *t;
179e01402b1SRalf Baechle 
180e01402b1SRalf Baechle 	list_for_each_entry(t, &vpecontrol.tc_list, list) {
181e01402b1SRalf Baechle 		if (t->index == index)
182e01402b1SRalf Baechle 			return t;
183e01402b1SRalf Baechle 	}
184e01402b1SRalf Baechle 
185e01402b1SRalf Baechle 	return NULL;
186e01402b1SRalf Baechle }
187e01402b1SRalf Baechle 
188e01402b1SRalf Baechle struct tc *get_tc_unused(void)
189e01402b1SRalf Baechle {
190e01402b1SRalf Baechle 	struct tc *t;
191e01402b1SRalf Baechle 
192e01402b1SRalf Baechle 	list_for_each_entry(t, &vpecontrol.tc_list, list) {
193e01402b1SRalf Baechle 		if (t->state == TC_STATE_UNUSED)
194e01402b1SRalf Baechle 			return t;
195e01402b1SRalf Baechle 	}
196e01402b1SRalf Baechle 
197e01402b1SRalf Baechle 	return NULL;
198e01402b1SRalf Baechle }
199e01402b1SRalf Baechle 
200e01402b1SRalf Baechle /* allocate a vpe and associate it with this minor (or index) */
201e01402b1SRalf Baechle struct vpe *alloc_vpe(int minor)
202e01402b1SRalf Baechle {
203e01402b1SRalf Baechle 	struct vpe *v;
204e01402b1SRalf Baechle 
205307bd284SRalf Baechle 	if ((v = kzalloc(sizeof(struct vpe), GFP_KERNEL)) == NULL) {
206e01402b1SRalf Baechle 		return NULL;
207e01402b1SRalf Baechle 	}
208e01402b1SRalf Baechle 
209e01402b1SRalf Baechle 	INIT_LIST_HEAD(&v->tc);
210e01402b1SRalf Baechle 	list_add_tail(&v->list, &vpecontrol.vpe_list);
211e01402b1SRalf Baechle 
2122600990eSRalf Baechle 	INIT_LIST_HEAD(&v->notify);
213e01402b1SRalf Baechle 	v->minor = minor;
214e01402b1SRalf Baechle 	return v;
215e01402b1SRalf Baechle }
216e01402b1SRalf Baechle 
217e01402b1SRalf Baechle /* allocate a tc. At startup only tc0 is running, all other can be halted. */
218e01402b1SRalf Baechle struct tc *alloc_tc(int index)
219e01402b1SRalf Baechle {
22007cc0c9eSRalf Baechle 	struct tc *tc;
221e01402b1SRalf Baechle 
22207cc0c9eSRalf Baechle 	if ((tc = kzalloc(sizeof(struct tc), GFP_KERNEL)) == NULL)
22307cc0c9eSRalf Baechle 		goto out;
224e01402b1SRalf Baechle 
22507cc0c9eSRalf Baechle 	INIT_LIST_HEAD(&tc->tc);
22607cc0c9eSRalf Baechle 	tc->index = index;
22707cc0c9eSRalf Baechle 	list_add_tail(&tc->list, &vpecontrol.tc_list);
228e01402b1SRalf Baechle 
22907cc0c9eSRalf Baechle out:
23007cc0c9eSRalf Baechle 	return tc;
231e01402b1SRalf Baechle }
232e01402b1SRalf Baechle 
233e01402b1SRalf Baechle /* clean up and free everything */
234e01402b1SRalf Baechle void release_vpe(struct vpe *v)
235e01402b1SRalf Baechle {
236e01402b1SRalf Baechle 	list_del(&v->list);
237e01402b1SRalf Baechle 	if (v->load_addr)
238e01402b1SRalf Baechle 		release_progmem(v);
239e01402b1SRalf Baechle 	kfree(v);
240e01402b1SRalf Baechle }
241e01402b1SRalf Baechle 
242e01402b1SRalf Baechle void dump_mtregs(void)
243e01402b1SRalf Baechle {
244e01402b1SRalf Baechle 	unsigned long val;
245e01402b1SRalf Baechle 
246e01402b1SRalf Baechle 	val = read_c0_config3();
247e01402b1SRalf Baechle 	printk("config3 0x%lx MT %ld\n", val,
248e01402b1SRalf Baechle 	       (val & CONFIG3_MT) >> CONFIG3_MT_SHIFT);
249e01402b1SRalf Baechle 
250e01402b1SRalf Baechle 	val = read_c0_mvpcontrol();
251e01402b1SRalf Baechle 	printk("MVPControl 0x%lx, STLB %ld VPC %ld EVP %ld\n", val,
252e01402b1SRalf Baechle 	       (val & MVPCONTROL_STLB) >> MVPCONTROL_STLB_SHIFT,
253e01402b1SRalf Baechle 	       (val & MVPCONTROL_VPC) >> MVPCONTROL_VPC_SHIFT,
254e01402b1SRalf Baechle 	       (val & MVPCONTROL_EVP));
255e01402b1SRalf Baechle 
2562600990eSRalf Baechle 	val = read_c0_mvpconf0();
2572600990eSRalf Baechle 	printk("mvpconf0 0x%lx, PVPE %ld PTC %ld M %ld\n", val,
2582600990eSRalf Baechle 	       (val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT,
2592600990eSRalf Baechle 	       val & MVPCONF0_PTC, (val & MVPCONF0_M) >> MVPCONF0_M_SHIFT);
260e01402b1SRalf Baechle }
261e01402b1SRalf Baechle 
262e01402b1SRalf Baechle /* Find some VPE program space  */
263571e0bedSRalf Baechle static void *alloc_progmem(unsigned long len)
264e01402b1SRalf Baechle {
2655408c490SRalf Baechle 	void *addr;
2665408c490SRalf Baechle 
267e01402b1SRalf Baechle #ifdef CONFIG_MIPS_VPE_LOADER_TOM
2685408c490SRalf Baechle 	/*
2695408c490SRalf Baechle 	 * This means you must tell Linux to use less memory than you
2705408c490SRalf Baechle 	 * physically have, for example by passing a mem= boot argument.
2715408c490SRalf Baechle 	 */
2725408c490SRalf Baechle 	addr = pfn_to_kaddr(max_pfn);
2735408c490SRalf Baechle 	memset(addr, 0, len);
274e01402b1SRalf Baechle #else
2755408c490SRalf Baechle 	/* simple grab some mem for now */
2765408c490SRalf Baechle 	addr = kzalloc(len, GFP_KERNEL);
277e01402b1SRalf Baechle #endif
2785408c490SRalf Baechle 
2795408c490SRalf Baechle 	return addr;
280e01402b1SRalf Baechle }
281e01402b1SRalf Baechle 
282e01402b1SRalf Baechle static void release_progmem(void *ptr)
283e01402b1SRalf Baechle {
284e01402b1SRalf Baechle #ifndef CONFIG_MIPS_VPE_LOADER_TOM
285e01402b1SRalf Baechle 	kfree(ptr);
286e01402b1SRalf Baechle #endif
287e01402b1SRalf Baechle }
288e01402b1SRalf Baechle 
289e01402b1SRalf Baechle /* Update size with this section: return offset. */
290e01402b1SRalf Baechle static long get_offset(unsigned long *size, Elf_Shdr * sechdr)
291e01402b1SRalf Baechle {
292e01402b1SRalf Baechle 	long ret;
293e01402b1SRalf Baechle 
294e01402b1SRalf Baechle 	ret = ALIGN(*size, sechdr->sh_addralign ? : 1);
295e01402b1SRalf Baechle 	*size = ret + sechdr->sh_size;
296e01402b1SRalf Baechle 	return ret;
297e01402b1SRalf Baechle }
298e01402b1SRalf Baechle 
299e01402b1SRalf Baechle /* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld
300e01402b1SRalf Baechle    might -- code, read-only data, read-write data, small data.  Tally
301e01402b1SRalf Baechle    sizes, and place the offsets into sh_entsize fields: high bit means it
302e01402b1SRalf Baechle    belongs in init. */
303e01402b1SRalf Baechle static void layout_sections(struct module *mod, const Elf_Ehdr * hdr,
304e01402b1SRalf Baechle 			    Elf_Shdr * sechdrs, const char *secstrings)
305e01402b1SRalf Baechle {
306e01402b1SRalf Baechle 	static unsigned long const masks[][2] = {
307e01402b1SRalf Baechle 		/* NOTE: all executable code must be the first section
308e01402b1SRalf Baechle 		 * in this array; otherwise modify the text_size
309e01402b1SRalf Baechle 		 * finder in the two loops below */
310e01402b1SRalf Baechle 		{SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL},
311e01402b1SRalf Baechle 		{SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL},
312e01402b1SRalf Baechle 		{SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL},
313e01402b1SRalf Baechle 		{ARCH_SHF_SMALL | SHF_ALLOC, 0}
314e01402b1SRalf Baechle 	};
315e01402b1SRalf Baechle 	unsigned int m, i;
316e01402b1SRalf Baechle 
317e01402b1SRalf Baechle 	for (i = 0; i < hdr->e_shnum; i++)
318e01402b1SRalf Baechle 		sechdrs[i].sh_entsize = ~0UL;
319e01402b1SRalf Baechle 
320e01402b1SRalf Baechle 	for (m = 0; m < ARRAY_SIZE(masks); ++m) {
321e01402b1SRalf Baechle 		for (i = 0; i < hdr->e_shnum; ++i) {
322e01402b1SRalf Baechle 			Elf_Shdr *s = &sechdrs[i];
323e01402b1SRalf Baechle 
324e01402b1SRalf Baechle 			//  || strncmp(secstrings + s->sh_name, ".init", 5) == 0)
325e01402b1SRalf Baechle 			if ((s->sh_flags & masks[m][0]) != masks[m][0]
326e01402b1SRalf Baechle 			    || (s->sh_flags & masks[m][1])
327e01402b1SRalf Baechle 			    || s->sh_entsize != ~0UL)
328e01402b1SRalf Baechle 				continue;
329e01402b1SRalf Baechle 			s->sh_entsize = get_offset(&mod->core_size, s);
330e01402b1SRalf Baechle 		}
331e01402b1SRalf Baechle 
332e01402b1SRalf Baechle 		if (m == 0)
333e01402b1SRalf Baechle 			mod->core_text_size = mod->core_size;
334e01402b1SRalf Baechle 
335e01402b1SRalf Baechle 	}
336e01402b1SRalf Baechle }
337e01402b1SRalf Baechle 
338e01402b1SRalf Baechle 
339e01402b1SRalf Baechle /* from module-elf32.c, but subverted a little */
340e01402b1SRalf Baechle 
341e01402b1SRalf Baechle struct mips_hi16 {
342e01402b1SRalf Baechle 	struct mips_hi16 *next;
343e01402b1SRalf Baechle 	Elf32_Addr *addr;
344e01402b1SRalf Baechle 	Elf32_Addr value;
345e01402b1SRalf Baechle };
346e01402b1SRalf Baechle 
347e01402b1SRalf Baechle static struct mips_hi16 *mips_hi16_list;
348e01402b1SRalf Baechle static unsigned int gp_offs, gp_addr;
349e01402b1SRalf Baechle 
350e01402b1SRalf Baechle static int apply_r_mips_none(struct module *me, uint32_t *location,
351e01402b1SRalf Baechle 			     Elf32_Addr v)
352e01402b1SRalf Baechle {
353e01402b1SRalf Baechle 	return 0;
354e01402b1SRalf Baechle }
355e01402b1SRalf Baechle 
356e01402b1SRalf Baechle static int apply_r_mips_gprel16(struct module *me, uint32_t *location,
357e01402b1SRalf Baechle 				Elf32_Addr v)
358e01402b1SRalf Baechle {
359e01402b1SRalf Baechle 	int rel;
360e01402b1SRalf Baechle 
361e01402b1SRalf Baechle 	if( !(*location & 0xffff) ) {
362e01402b1SRalf Baechle 		rel = (int)v - gp_addr;
363e01402b1SRalf Baechle 	}
364e01402b1SRalf Baechle 	else {
365e01402b1SRalf Baechle 		/* .sbss + gp(relative) + offset */
366e01402b1SRalf Baechle 		/* kludge! */
367e01402b1SRalf Baechle 		rel =  (int)(short)((int)v + gp_offs +
368e01402b1SRalf Baechle 				    (int)(short)(*location & 0xffff) - gp_addr);
369e01402b1SRalf Baechle 	}
370e01402b1SRalf Baechle 
371e01402b1SRalf Baechle 	if( (rel > 32768) || (rel < -32768) ) {
3722600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: apply_r_mips_gprel16: "
3732600990eSRalf Baechle 		       "relative address 0x%x out of range of gp register\n",
3742600990eSRalf Baechle 		       rel);
375e01402b1SRalf Baechle 		return -ENOEXEC;
376e01402b1SRalf Baechle 	}
377e01402b1SRalf Baechle 
378e01402b1SRalf Baechle 	*location = (*location & 0xffff0000) | (rel & 0xffff);
379e01402b1SRalf Baechle 
380e01402b1SRalf Baechle 	return 0;
381e01402b1SRalf Baechle }
382e01402b1SRalf Baechle 
383e01402b1SRalf Baechle static int apply_r_mips_pc16(struct module *me, uint32_t *location,
384e01402b1SRalf Baechle 			     Elf32_Addr v)
385e01402b1SRalf Baechle {
386e01402b1SRalf Baechle 	int rel;
387e01402b1SRalf Baechle 	rel = (((unsigned int)v - (unsigned int)location));
388e01402b1SRalf Baechle 	rel >>= 2;		// because the offset is in _instructions_ not bytes.
389e01402b1SRalf Baechle 	rel -= 1;		// and one instruction less due to the branch delay slot.
390e01402b1SRalf Baechle 
391e01402b1SRalf Baechle 	if( (rel > 32768) || (rel < -32768) ) {
3922600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: "
393e01402b1SRalf Baechle  		       "apply_r_mips_pc16: relative address out of range 0x%x\n", rel);
394e01402b1SRalf Baechle 		return -ENOEXEC;
395e01402b1SRalf Baechle 	}
396e01402b1SRalf Baechle 
397e01402b1SRalf Baechle 	*location = (*location & 0xffff0000) | (rel & 0xffff);
398e01402b1SRalf Baechle 
399e01402b1SRalf Baechle 	return 0;
400e01402b1SRalf Baechle }
401e01402b1SRalf Baechle 
402e01402b1SRalf Baechle static int apply_r_mips_32(struct module *me, uint32_t *location,
403e01402b1SRalf Baechle 			   Elf32_Addr v)
404e01402b1SRalf Baechle {
405e01402b1SRalf Baechle 	*location += v;
406e01402b1SRalf Baechle 
407e01402b1SRalf Baechle 	return 0;
408e01402b1SRalf Baechle }
409e01402b1SRalf Baechle 
410e01402b1SRalf Baechle static int apply_r_mips_26(struct module *me, uint32_t *location,
411e01402b1SRalf Baechle 			   Elf32_Addr v)
412e01402b1SRalf Baechle {
413e01402b1SRalf Baechle 	if (v % 4) {
4142600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: apply_r_mips_26 "
4152600990eSRalf Baechle 		       " unaligned relocation\n");
416e01402b1SRalf Baechle 		return -ENOEXEC;
417e01402b1SRalf Baechle 	}
418e01402b1SRalf Baechle 
419307bd284SRalf Baechle /*
420307bd284SRalf Baechle  * Not desperately convinced this is a good check of an overflow condition
421307bd284SRalf Baechle  * anyway. But it gets in the way of handling undefined weak symbols which
422307bd284SRalf Baechle  * we want to set to zero.
423307bd284SRalf Baechle  * if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) {
424307bd284SRalf Baechle  * printk(KERN_ERR
425307bd284SRalf Baechle  * "module %s: relocation overflow\n",
426307bd284SRalf Baechle  * me->name);
427307bd284SRalf Baechle  * return -ENOEXEC;
428307bd284SRalf Baechle  * }
429e01402b1SRalf Baechle  */
430e01402b1SRalf Baechle 
431e01402b1SRalf Baechle 	*location = (*location & ~0x03ffffff) |
432e01402b1SRalf Baechle 		((*location + (v >> 2)) & 0x03ffffff);
433e01402b1SRalf Baechle 	return 0;
434e01402b1SRalf Baechle }
435e01402b1SRalf Baechle 
436e01402b1SRalf Baechle static int apply_r_mips_hi16(struct module *me, uint32_t *location,
437e01402b1SRalf Baechle 			     Elf32_Addr v)
438e01402b1SRalf Baechle {
439e01402b1SRalf Baechle 	struct mips_hi16 *n;
440e01402b1SRalf Baechle 
441e01402b1SRalf Baechle 	/*
442e01402b1SRalf Baechle 	 * We cannot relocate this one now because we don't know the value of
443e01402b1SRalf Baechle 	 * the carry we need to add.  Save the information, and let LO16 do the
444e01402b1SRalf Baechle 	 * actual relocation.
445e01402b1SRalf Baechle 	 */
446e01402b1SRalf Baechle 	n = kmalloc(sizeof *n, GFP_KERNEL);
447e01402b1SRalf Baechle 	if (!n)
448e01402b1SRalf Baechle 		return -ENOMEM;
449e01402b1SRalf Baechle 
450e01402b1SRalf Baechle 	n->addr = location;
451e01402b1SRalf Baechle 	n->value = v;
452e01402b1SRalf Baechle 	n->next = mips_hi16_list;
453e01402b1SRalf Baechle 	mips_hi16_list = n;
454e01402b1SRalf Baechle 
455e01402b1SRalf Baechle 	return 0;
456e01402b1SRalf Baechle }
457e01402b1SRalf Baechle 
458e01402b1SRalf Baechle static int apply_r_mips_lo16(struct module *me, uint32_t *location,
459e01402b1SRalf Baechle 			     Elf32_Addr v)
460e01402b1SRalf Baechle {
461e01402b1SRalf Baechle 	unsigned long insnlo = *location;
462e01402b1SRalf Baechle 	Elf32_Addr val, vallo;
463e01402b1SRalf Baechle 
464e01402b1SRalf Baechle 	/* Sign extend the addend we extract from the lo insn.  */
465e01402b1SRalf Baechle 	vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;
466e01402b1SRalf Baechle 
467e01402b1SRalf Baechle 	if (mips_hi16_list != NULL) {
468e01402b1SRalf Baechle 		struct mips_hi16 *l;
469e01402b1SRalf Baechle 
470e01402b1SRalf Baechle 		l = mips_hi16_list;
471e01402b1SRalf Baechle 		while (l != NULL) {
472e01402b1SRalf Baechle 			struct mips_hi16 *next;
473e01402b1SRalf Baechle 			unsigned long insn;
474e01402b1SRalf Baechle 
475e01402b1SRalf Baechle 			/*
476e01402b1SRalf Baechle 			 * The value for the HI16 had best be the same.
477e01402b1SRalf Baechle 			 */
478e01402b1SRalf Baechle  			if (v != l->value) {
4792600990eSRalf Baechle 				printk(KERN_DEBUG "VPE loader: "
480b1e3afa0SJoe Perches 				       "apply_r_mips_lo16/hi16: \t"
4812600990eSRalf Baechle 				       "inconsistent value information\n");
4822600990eSRalf Baechle 				return -ENOEXEC;
483e01402b1SRalf Baechle 			}
484e01402b1SRalf Baechle 
485e01402b1SRalf Baechle 			/*
486e01402b1SRalf Baechle 			 * Do the HI16 relocation.  Note that we actually don't
487e01402b1SRalf Baechle 			 * need to know anything about the LO16 itself, except
488e01402b1SRalf Baechle 			 * where to find the low 16 bits of the addend needed
489e01402b1SRalf Baechle 			 * by the LO16.
490e01402b1SRalf Baechle 			 */
491e01402b1SRalf Baechle 			insn = *l->addr;
492e01402b1SRalf Baechle 			val = ((insn & 0xffff) << 16) + vallo;
493e01402b1SRalf Baechle 			val += v;
494e01402b1SRalf Baechle 
495e01402b1SRalf Baechle 			/*
496e01402b1SRalf Baechle 			 * Account for the sign extension that will happen in
497e01402b1SRalf Baechle 			 * the low bits.
498e01402b1SRalf Baechle 			 */
499e01402b1SRalf Baechle 			val = ((val >> 16) + ((val & 0x8000) != 0)) & 0xffff;
500e01402b1SRalf Baechle 
501e01402b1SRalf Baechle 			insn = (insn & ~0xffff) | val;
502e01402b1SRalf Baechle 			*l->addr = insn;
503e01402b1SRalf Baechle 
504e01402b1SRalf Baechle 			next = l->next;
505e01402b1SRalf Baechle 			kfree(l);
506e01402b1SRalf Baechle 			l = next;
507e01402b1SRalf Baechle 		}
508e01402b1SRalf Baechle 
509e01402b1SRalf Baechle 		mips_hi16_list = NULL;
510e01402b1SRalf Baechle 	}
511e01402b1SRalf Baechle 
512e01402b1SRalf Baechle 	/*
513e01402b1SRalf Baechle 	 * Ok, we're done with the HI16 relocs.  Now deal with the LO16.
514e01402b1SRalf Baechle 	 */
515e01402b1SRalf Baechle 	val = v + vallo;
516e01402b1SRalf Baechle 	insnlo = (insnlo & ~0xffff) | (val & 0xffff);
517e01402b1SRalf Baechle 	*location = insnlo;
518e01402b1SRalf Baechle 
519e01402b1SRalf Baechle 	return 0;
520e01402b1SRalf Baechle }
521e01402b1SRalf Baechle 
522e01402b1SRalf Baechle static int (*reloc_handlers[]) (struct module *me, uint32_t *location,
523e01402b1SRalf Baechle 				Elf32_Addr v) = {
524e01402b1SRalf Baechle 	[R_MIPS_NONE]	= apply_r_mips_none,
525e01402b1SRalf Baechle 	[R_MIPS_32]	= apply_r_mips_32,
526e01402b1SRalf Baechle 	[R_MIPS_26]	= apply_r_mips_26,
527e01402b1SRalf Baechle 	[R_MIPS_HI16]	= apply_r_mips_hi16,
528e01402b1SRalf Baechle 	[R_MIPS_LO16]	= apply_r_mips_lo16,
529e01402b1SRalf Baechle 	[R_MIPS_GPREL16] = apply_r_mips_gprel16,
530e01402b1SRalf Baechle 	[R_MIPS_PC16] = apply_r_mips_pc16
531e01402b1SRalf Baechle };
532e01402b1SRalf Baechle 
5332600990eSRalf Baechle static char *rstrs[] = {
5342600990eSRalf Baechle 	[R_MIPS_NONE]	= "MIPS_NONE",
5352600990eSRalf Baechle 	[R_MIPS_32]	= "MIPS_32",
5362600990eSRalf Baechle 	[R_MIPS_26]	= "MIPS_26",
5372600990eSRalf Baechle 	[R_MIPS_HI16]	= "MIPS_HI16",
5382600990eSRalf Baechle 	[R_MIPS_LO16]	= "MIPS_LO16",
5392600990eSRalf Baechle 	[R_MIPS_GPREL16] = "MIPS_GPREL16",
5402600990eSRalf Baechle 	[R_MIPS_PC16] = "MIPS_PC16"
5412600990eSRalf Baechle };
542e01402b1SRalf Baechle 
543e01402b1SRalf Baechle int apply_relocations(Elf32_Shdr *sechdrs,
544e01402b1SRalf Baechle 		      const char *strtab,
545e01402b1SRalf Baechle 		      unsigned int symindex,
546e01402b1SRalf Baechle 		      unsigned int relsec,
547e01402b1SRalf Baechle 		      struct module *me)
548e01402b1SRalf Baechle {
549e01402b1SRalf Baechle 	Elf32_Rel *rel = (void *) sechdrs[relsec].sh_addr;
550e01402b1SRalf Baechle 	Elf32_Sym *sym;
551e01402b1SRalf Baechle 	uint32_t *location;
552e01402b1SRalf Baechle 	unsigned int i;
553e01402b1SRalf Baechle 	Elf32_Addr v;
554e01402b1SRalf Baechle 	int res;
555e01402b1SRalf Baechle 
556e01402b1SRalf Baechle 	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
557e01402b1SRalf Baechle 		Elf32_Word r_info = rel[i].r_info;
558e01402b1SRalf Baechle 
559e01402b1SRalf Baechle 		/* This is where to make the change */
560e01402b1SRalf Baechle 		location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
561e01402b1SRalf Baechle 			+ rel[i].r_offset;
562e01402b1SRalf Baechle 		/* This is the symbol it is referring to */
563e01402b1SRalf Baechle 		sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
564e01402b1SRalf Baechle 			+ ELF32_R_SYM(r_info);
565e01402b1SRalf Baechle 
566e01402b1SRalf Baechle 		if (!sym->st_value) {
567e01402b1SRalf Baechle 			printk(KERN_DEBUG "%s: undefined weak symbol %s\n",
568e01402b1SRalf Baechle 			       me->name, strtab + sym->st_name);
569e01402b1SRalf Baechle 			/* just print the warning, dont barf */
570e01402b1SRalf Baechle 		}
571e01402b1SRalf Baechle 
572e01402b1SRalf Baechle 		v = sym->st_value;
573e01402b1SRalf Baechle 
574e01402b1SRalf Baechle 		res = reloc_handlers[ELF32_R_TYPE(r_info)](me, location, v);
575e01402b1SRalf Baechle 		if( res ) {
5762600990eSRalf Baechle 			char *r = rstrs[ELF32_R_TYPE(r_info)];
5772600990eSRalf Baechle 		    	printk(KERN_WARNING "VPE loader: .text+0x%x "
5782600990eSRalf Baechle 			       "relocation type %s for symbol \"%s\" failed\n",
5792600990eSRalf Baechle 			       rel[i].r_offset, r ? r : "UNKNOWN",
5802600990eSRalf Baechle 			       strtab + sym->st_name);
581e01402b1SRalf Baechle 			return res;
582e01402b1SRalf Baechle 		}
5832600990eSRalf Baechle 	}
584e01402b1SRalf Baechle 
585e01402b1SRalf Baechle 	return 0;
586e01402b1SRalf Baechle }
587e01402b1SRalf Baechle 
588e01402b1SRalf Baechle void save_gp_address(unsigned int secbase, unsigned int rel)
589e01402b1SRalf Baechle {
590e01402b1SRalf Baechle 	gp_addr = secbase + rel;
591e01402b1SRalf Baechle 	gp_offs = gp_addr - (secbase & 0xffff0000);
592e01402b1SRalf Baechle }
593e01402b1SRalf Baechle /* end module-elf32.c */
594e01402b1SRalf Baechle 
595e01402b1SRalf Baechle 
596e01402b1SRalf Baechle 
597e01402b1SRalf Baechle /* Change all symbols so that sh_value encodes the pointer directly. */
5982600990eSRalf Baechle static void simplify_symbols(Elf_Shdr * sechdrs,
599e01402b1SRalf Baechle 			    unsigned int symindex,
600e01402b1SRalf Baechle 			    const char *strtab,
601e01402b1SRalf Baechle 			    const char *secstrings,
602e01402b1SRalf Baechle 			    unsigned int nsecs, struct module *mod)
603e01402b1SRalf Baechle {
604e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
605e01402b1SRalf Baechle 	unsigned long secbase, bssbase = 0;
606e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
6072600990eSRalf Baechle 	int size;
608e01402b1SRalf Baechle 
609e01402b1SRalf Baechle 	/* find the .bss section for COMMON symbols */
610e01402b1SRalf Baechle 	for (i = 0; i < nsecs; i++) {
6112600990eSRalf Baechle 		if (strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) == 0) {
612e01402b1SRalf Baechle 			bssbase = sechdrs[i].sh_addr;
6132600990eSRalf Baechle 			break;
6142600990eSRalf Baechle 		}
615e01402b1SRalf Baechle 	}
616e01402b1SRalf Baechle 
617e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
618e01402b1SRalf Baechle 		switch (sym[i].st_shndx) {
619e01402b1SRalf Baechle 		case SHN_COMMON:
6202600990eSRalf Baechle 			/* Allocate space for the symbol in the .bss section.
6212600990eSRalf Baechle 			   st_value is currently size.
622e01402b1SRalf Baechle 			   We want it to have the address of the symbol. */
623e01402b1SRalf Baechle 
624e01402b1SRalf Baechle 			size = sym[i].st_value;
625e01402b1SRalf Baechle 			sym[i].st_value = bssbase;
626e01402b1SRalf Baechle 
627e01402b1SRalf Baechle 			bssbase += size;
628e01402b1SRalf Baechle 			break;
629e01402b1SRalf Baechle 
630e01402b1SRalf Baechle 		case SHN_ABS:
631e01402b1SRalf Baechle 			/* Don't need to do anything */
632e01402b1SRalf Baechle 			break;
633e01402b1SRalf Baechle 
634e01402b1SRalf Baechle 		case SHN_UNDEF:
635e01402b1SRalf Baechle 			/* ret = -ENOENT; */
636e01402b1SRalf Baechle 			break;
637e01402b1SRalf Baechle 
638e01402b1SRalf Baechle 		case SHN_MIPS_SCOMMON:
6392600990eSRalf Baechle 			printk(KERN_DEBUG "simplify_symbols: ignoring SHN_MIPS_SCOMMON "
6402600990eSRalf Baechle 			       "symbol <%s> st_shndx %d\n", strtab + sym[i].st_name,
6412600990eSRalf Baechle 			       sym[i].st_shndx);
642e01402b1SRalf Baechle 			// .sbss section
643e01402b1SRalf Baechle 			break;
644e01402b1SRalf Baechle 
645e01402b1SRalf Baechle 		default:
646e01402b1SRalf Baechle 			secbase = sechdrs[sym[i].st_shndx].sh_addr;
647e01402b1SRalf Baechle 
648e01402b1SRalf Baechle 			if (strncmp(strtab + sym[i].st_name, "_gp", 3) == 0) {
649e01402b1SRalf Baechle 				save_gp_address(secbase, sym[i].st_value);
650e01402b1SRalf Baechle 			}
651e01402b1SRalf Baechle 
652e01402b1SRalf Baechle 			sym[i].st_value += secbase;
653e01402b1SRalf Baechle 			break;
654e01402b1SRalf Baechle 		}
655e01402b1SRalf Baechle 	}
656e01402b1SRalf Baechle }
657e01402b1SRalf Baechle 
658e01402b1SRalf Baechle #ifdef DEBUG_ELFLOADER
659e01402b1SRalf Baechle static void dump_elfsymbols(Elf_Shdr * sechdrs, unsigned int symindex,
660e01402b1SRalf Baechle 			    const char *strtab, struct module *mod)
661e01402b1SRalf Baechle {
662e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
663e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
664e01402b1SRalf Baechle 
665e01402b1SRalf Baechle 	printk(KERN_DEBUG "dump_elfsymbols: n %d\n", n);
666e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
667e01402b1SRalf Baechle 		printk(KERN_DEBUG " i %d name <%s> 0x%x\n", i,
668e01402b1SRalf Baechle 		       strtab + sym[i].st_name, sym[i].st_value);
669e01402b1SRalf Baechle 	}
670e01402b1SRalf Baechle }
671e01402b1SRalf Baechle #endif
672e01402b1SRalf Baechle 
673e01402b1SRalf Baechle /* We are prepared so configure and start the VPE... */
674be6e1437SRalf Baechle static int vpe_run(struct vpe * v)
675e01402b1SRalf Baechle {
67607cc0c9eSRalf Baechle 	unsigned long flags, val, dmt_flag;
6772600990eSRalf Baechle 	struct vpe_notifications *n;
67807cc0c9eSRalf Baechle 	unsigned int vpeflags;
679e01402b1SRalf Baechle 	struct tc *t;
680e01402b1SRalf Baechle 
681e01402b1SRalf Baechle 	/* check we are the Master VPE */
68207cc0c9eSRalf Baechle 	local_irq_save(flags);
683e01402b1SRalf Baechle 	val = read_c0_vpeconf0();
684e01402b1SRalf Baechle 	if (!(val & VPECONF0_MVP)) {
685e01402b1SRalf Baechle 		printk(KERN_WARNING
6862600990eSRalf Baechle 		       "VPE loader: only Master VPE's are allowed to configure MT\n");
68707cc0c9eSRalf Baechle 		local_irq_restore(flags);
68807cc0c9eSRalf Baechle 
689e01402b1SRalf Baechle 		return -1;
690e01402b1SRalf Baechle 	}
691e01402b1SRalf Baechle 
69207cc0c9eSRalf Baechle 	dmt_flag = dmt();
69307cc0c9eSRalf Baechle 	vpeflags = dvpe();
694e01402b1SRalf Baechle 
695e01402b1SRalf Baechle 	if (!list_empty(&v->tc)) {
696e01402b1SRalf Baechle 		if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
69707cc0c9eSRalf Baechle 			evpe(vpeflags);
69807cc0c9eSRalf Baechle 			emt(dmt_flag);
69907cc0c9eSRalf Baechle 			local_irq_restore(flags);
70007cc0c9eSRalf Baechle 
70107cc0c9eSRalf Baechle 			printk(KERN_WARNING
70207cc0c9eSRalf Baechle 			       "VPE loader: TC %d is already in use.\n",
703e01402b1SRalf Baechle                                t->index);
704e01402b1SRalf Baechle 			return -ENOEXEC;
705e01402b1SRalf Baechle 		}
706e01402b1SRalf Baechle 	} else {
70707cc0c9eSRalf Baechle 		evpe(vpeflags);
70807cc0c9eSRalf Baechle 		emt(dmt_flag);
70907cc0c9eSRalf Baechle 		local_irq_restore(flags);
71007cc0c9eSRalf Baechle 
71107cc0c9eSRalf Baechle 		printk(KERN_WARNING
71207cc0c9eSRalf Baechle 		       "VPE loader: No TC's associated with VPE %d\n",
713e01402b1SRalf Baechle 		       v->minor);
71407cc0c9eSRalf Baechle 
715e01402b1SRalf Baechle 		return -ENOEXEC;
716e01402b1SRalf Baechle 	}
717e01402b1SRalf Baechle 
7182600990eSRalf Baechle 	/* Put MVPE's into 'configuration state' */
7192600990eSRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
720e01402b1SRalf Baechle 
7212600990eSRalf Baechle 	settc(t->index);
722e01402b1SRalf Baechle 
723e01402b1SRalf Baechle 	/* should check it is halted, and not activated */
724e01402b1SRalf Baechle 	if ((read_tc_c0_tcstatus() & TCSTATUS_A) || !(read_tc_c0_tchalt() & TCHALT_H)) {
72507cc0c9eSRalf Baechle 		evpe(vpeflags);
72607cc0c9eSRalf Baechle 		emt(dmt_flag);
72707cc0c9eSRalf Baechle 		local_irq_restore(flags);
72807cc0c9eSRalf Baechle 
72907cc0c9eSRalf Baechle 		printk(KERN_WARNING "VPE loader: TC %d is already active!\n",
730e01402b1SRalf Baechle 		       t->index);
73107cc0c9eSRalf Baechle 
732e01402b1SRalf Baechle 		return -ENOEXEC;
733e01402b1SRalf Baechle 	}
734e01402b1SRalf Baechle 
735e01402b1SRalf Baechle 	/* Write the address we want it to start running from in the TCPC register. */
736e01402b1SRalf Baechle 	write_tc_c0_tcrestart((unsigned long)v->__start);
737e01402b1SRalf Baechle 	write_tc_c0_tccontext((unsigned long)0);
73807cc0c9eSRalf Baechle 
7392600990eSRalf Baechle 	/*
7402600990eSRalf Baechle 	 * Mark the TC as activated, not interrupt exempt and not dynamically
7412600990eSRalf Baechle 	 * allocatable
7422600990eSRalf Baechle 	 */
743e01402b1SRalf Baechle 	val = read_tc_c0_tcstatus();
744e01402b1SRalf Baechle 	val = (val & ~(TCSTATUS_DA | TCSTATUS_IXMT)) | TCSTATUS_A;
745e01402b1SRalf Baechle 	write_tc_c0_tcstatus(val);
746e01402b1SRalf Baechle 
747e01402b1SRalf Baechle 	write_tc_c0_tchalt(read_tc_c0_tchalt() & ~TCHALT_H);
748e01402b1SRalf Baechle 
749e01402b1SRalf Baechle 	/*
750e01402b1SRalf Baechle 	 * The sde-kit passes 'memsize' to __start in $a3, so set something
7512600990eSRalf Baechle 	 * here...  Or set $a3 to zero and define DFLT_STACK_SIZE and
752e01402b1SRalf Baechle 	 * DFLT_HEAP_SIZE when you compile your program
753e01402b1SRalf Baechle 	 */
75441790e04SRalf Baechle 	mttgpr(6, v->ntcs);
7552600990eSRalf Baechle 	mttgpr(7, physical_memsize);
756e01402b1SRalf Baechle 
7572600990eSRalf Baechle 	/* set up VPE1 */
7582600990eSRalf Baechle 	/*
7592600990eSRalf Baechle 	 * bind the TC to VPE 1 as late as possible so we only have the final
7602600990eSRalf Baechle 	 * VPE registers to set up, and so an EJTAG probe can trigger on it
7612600990eSRalf Baechle 	 */
76207cc0c9eSRalf Baechle 	write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | 1);
7632600990eSRalf Baechle 
764a94d7020SElizabeth Oldham 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~(VPECONF0_VPA));
765a94d7020SElizabeth Oldham 
766a94d7020SElizabeth Oldham 	back_to_back_c0_hazard();
767a94d7020SElizabeth Oldham 
7682600990eSRalf Baechle 	/* Set up the XTC bit in vpeconf0 to point at our tc */
7692600990eSRalf Baechle 	write_vpe_c0_vpeconf0( (read_vpe_c0_vpeconf0() & ~(VPECONF0_XTC))
7702600990eSRalf Baechle 	                      | (t->index << VPECONF0_XTC_SHIFT));
7712600990eSRalf Baechle 
772a94d7020SElizabeth Oldham 	back_to_back_c0_hazard();
773a94d7020SElizabeth Oldham 
7742600990eSRalf Baechle 	/* enable this VPE */
7752600990eSRalf Baechle 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA);
776e01402b1SRalf Baechle 
777e01402b1SRalf Baechle 	/* clear out any left overs from a previous program */
7782600990eSRalf Baechle 	write_vpe_c0_status(0);
779e01402b1SRalf Baechle 	write_vpe_c0_cause(0);
780e01402b1SRalf Baechle 
781e01402b1SRalf Baechle 	/* take system out of configuration state */
782340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
783e01402b1SRalf Baechle 
784b618336aSKevin D. Kissell 	/*
785b618336aSKevin D. Kissell 	 * SMTC/SMVP kernels manage VPE enable independently,
786b618336aSKevin D. Kissell 	 * but uniprocessor kernels need to turn it on, even
787b618336aSKevin D. Kissell 	 * if that wasn't the pre-dvpe() state.
788b618336aSKevin D. Kissell 	 */
78907cc0c9eSRalf Baechle #ifdef CONFIG_SMP
79007cc0c9eSRalf Baechle 	evpe(vpeflags);
791b618336aSKevin D. Kissell #else
792b618336aSKevin D. Kissell 	evpe(EVPE_ENABLE);
79307cc0c9eSRalf Baechle #endif
79407cc0c9eSRalf Baechle 	emt(dmt_flag);
79507cc0c9eSRalf Baechle 	local_irq_restore(flags);
796e01402b1SRalf Baechle 
79707cc0c9eSRalf Baechle 	list_for_each_entry(n, &v->notify, list)
79807cc0c9eSRalf Baechle 		n->start(minor);
7992600990eSRalf Baechle 
800e01402b1SRalf Baechle 	return 0;
801e01402b1SRalf Baechle }
802e01402b1SRalf Baechle 
8032600990eSRalf Baechle static int find_vpe_symbols(struct vpe * v, Elf_Shdr * sechdrs,
804e01402b1SRalf Baechle 				      unsigned int symindex, const char *strtab,
805e01402b1SRalf Baechle 				      struct module *mod)
806e01402b1SRalf Baechle {
807e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
808e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
809e01402b1SRalf Baechle 
810e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
811e01402b1SRalf Baechle 		if (strcmp(strtab + sym[i].st_name, "__start") == 0) {
812e01402b1SRalf Baechle 			v->__start = sym[i].st_value;
813e01402b1SRalf Baechle 		}
814e01402b1SRalf Baechle 
815e01402b1SRalf Baechle 		if (strcmp(strtab + sym[i].st_name, "vpe_shared") == 0) {
816e01402b1SRalf Baechle 			v->shared_ptr = (void *)sym[i].st_value;
817e01402b1SRalf Baechle 		}
818e01402b1SRalf Baechle 	}
819e01402b1SRalf Baechle 
8202600990eSRalf Baechle 	if ( (v->__start == 0) || (v->shared_ptr == NULL))
8212600990eSRalf Baechle 		return -1;
8222600990eSRalf Baechle 
823e01402b1SRalf Baechle 	return 0;
824e01402b1SRalf Baechle }
825e01402b1SRalf Baechle 
826307bd284SRalf Baechle /*
8272600990eSRalf Baechle  * Allocates a VPE with some program code space(the load address), copies the
8282600990eSRalf Baechle  * contents of the program (p)buffer performing relocatations/etc, free's it
8292600990eSRalf Baechle  * when finished.
830e01402b1SRalf Baechle  */
831be6e1437SRalf Baechle static int vpe_elfload(struct vpe * v)
832e01402b1SRalf Baechle {
833e01402b1SRalf Baechle 	Elf_Ehdr *hdr;
834e01402b1SRalf Baechle 	Elf_Shdr *sechdrs;
835e01402b1SRalf Baechle 	long err = 0;
836e01402b1SRalf Baechle 	char *secstrings, *strtab = NULL;
8372600990eSRalf Baechle 	unsigned int len, i, symindex = 0, strindex = 0, relocate = 0;
838e01402b1SRalf Baechle 	struct module mod;	// so we can re-use the relocations code
839e01402b1SRalf Baechle 
840e01402b1SRalf Baechle 	memset(&mod, 0, sizeof(struct module));
8412600990eSRalf Baechle 	strcpy(mod.name, "VPE loader");
842e01402b1SRalf Baechle 
843e01402b1SRalf Baechle 	hdr = (Elf_Ehdr *) v->pbuffer;
844e01402b1SRalf Baechle 	len = v->plen;
845e01402b1SRalf Baechle 
846e01402b1SRalf Baechle 	/* Sanity checks against insmoding binaries or wrong arch,
847e01402b1SRalf Baechle 	   weird elf version */
848e01402b1SRalf Baechle 	if (memcmp(hdr->e_ident, ELFMAG, 4) != 0
8492600990eSRalf Baechle 	    || (hdr->e_type != ET_REL && hdr->e_type != ET_EXEC)
8502600990eSRalf Baechle 	    || !elf_check_arch(hdr)
851e01402b1SRalf Baechle 	    || hdr->e_shentsize != sizeof(*sechdrs)) {
852e01402b1SRalf Baechle 		printk(KERN_WARNING
8532600990eSRalf Baechle 		       "VPE loader: program wrong arch or weird elf version\n");
854e01402b1SRalf Baechle 
855e01402b1SRalf Baechle 		return -ENOEXEC;
856e01402b1SRalf Baechle 	}
857e01402b1SRalf Baechle 
8582600990eSRalf Baechle 	if (hdr->e_type == ET_REL)
8592600990eSRalf Baechle 		relocate = 1;
8602600990eSRalf Baechle 
861e01402b1SRalf Baechle 	if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) {
8622600990eSRalf Baechle 		printk(KERN_ERR "VPE loader: program length %u truncated\n",
8632600990eSRalf Baechle 		       len);
8642600990eSRalf Baechle 
865e01402b1SRalf Baechle 		return -ENOEXEC;
866e01402b1SRalf Baechle 	}
867e01402b1SRalf Baechle 
868e01402b1SRalf Baechle 	/* Convenience variables */
869e01402b1SRalf Baechle 	sechdrs = (void *)hdr + hdr->e_shoff;
870e01402b1SRalf Baechle 	secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
871e01402b1SRalf Baechle 	sechdrs[0].sh_addr = 0;
872e01402b1SRalf Baechle 
873e01402b1SRalf Baechle 	/* And these should exist, but gcc whinges if we don't init them */
874e01402b1SRalf Baechle 	symindex = strindex = 0;
875e01402b1SRalf Baechle 
8762600990eSRalf Baechle 	if (relocate) {
877e01402b1SRalf Baechle 		for (i = 1; i < hdr->e_shnum; i++) {
878e01402b1SRalf Baechle 			if (sechdrs[i].sh_type != SHT_NOBITS
879e01402b1SRalf Baechle 			    && len < sechdrs[i].sh_offset + sechdrs[i].sh_size) {
880e01402b1SRalf Baechle 				printk(KERN_ERR "VPE program length %u truncated\n",
881e01402b1SRalf Baechle 				       len);
882e01402b1SRalf Baechle 				return -ENOEXEC;
883e01402b1SRalf Baechle 			}
884e01402b1SRalf Baechle 
885e01402b1SRalf Baechle 			/* Mark all sections sh_addr with their address in the
886e01402b1SRalf Baechle 			   temporary image. */
887e01402b1SRalf Baechle 			sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
888e01402b1SRalf Baechle 
889e01402b1SRalf Baechle 			/* Internal symbols and strings. */
890e01402b1SRalf Baechle 			if (sechdrs[i].sh_type == SHT_SYMTAB) {
891e01402b1SRalf Baechle 				symindex = i;
892e01402b1SRalf Baechle 				strindex = sechdrs[i].sh_link;
893e01402b1SRalf Baechle 				strtab = (char *)hdr + sechdrs[strindex].sh_offset;
894e01402b1SRalf Baechle 			}
895e01402b1SRalf Baechle 		}
896e01402b1SRalf Baechle 		layout_sections(&mod, hdr, sechdrs, secstrings);
8972600990eSRalf Baechle 	}
898e01402b1SRalf Baechle 
899e01402b1SRalf Baechle 	v->load_addr = alloc_progmem(mod.core_size);
9005408c490SRalf Baechle 	if (!v->load_addr)
9015408c490SRalf Baechle 		return -ENOMEM;
902e01402b1SRalf Baechle 
9035408c490SRalf Baechle 	pr_info("VPE loader: loading to %p\n", v->load_addr);
904e01402b1SRalf Baechle 
9052600990eSRalf Baechle 	if (relocate) {
906e01402b1SRalf Baechle 		for (i = 0; i < hdr->e_shnum; i++) {
907e01402b1SRalf Baechle 			void *dest;
908e01402b1SRalf Baechle 
909e01402b1SRalf Baechle 			if (!(sechdrs[i].sh_flags & SHF_ALLOC))
910e01402b1SRalf Baechle 				continue;
911e01402b1SRalf Baechle 
912e01402b1SRalf Baechle 			dest = v->load_addr + sechdrs[i].sh_entsize;
913e01402b1SRalf Baechle 
914e01402b1SRalf Baechle 			if (sechdrs[i].sh_type != SHT_NOBITS)
915e01402b1SRalf Baechle 				memcpy(dest, (void *)sechdrs[i].sh_addr,
916e01402b1SRalf Baechle 				       sechdrs[i].sh_size);
917e01402b1SRalf Baechle 			/* Update sh_addr to point to copy in image. */
918e01402b1SRalf Baechle 			sechdrs[i].sh_addr = (unsigned long)dest;
9192600990eSRalf Baechle 
9202600990eSRalf Baechle 			printk(KERN_DEBUG " section sh_name %s sh_addr 0x%x\n",
9212600990eSRalf Baechle 			       secstrings + sechdrs[i].sh_name, sechdrs[i].sh_addr);
922e01402b1SRalf Baechle 		}
923e01402b1SRalf Baechle 
924e01402b1SRalf Baechle  		/* Fix up syms, so that st_value is a pointer to location. */
925e01402b1SRalf Baechle  		simplify_symbols(sechdrs, symindex, strtab, secstrings,
926e01402b1SRalf Baechle  				 hdr->e_shnum, &mod);
927e01402b1SRalf Baechle 
928e01402b1SRalf Baechle  		/* Now do relocations. */
929e01402b1SRalf Baechle  		for (i = 1; i < hdr->e_shnum; i++) {
930e01402b1SRalf Baechle  			const char *strtab = (char *)sechdrs[strindex].sh_addr;
931e01402b1SRalf Baechle  			unsigned int info = sechdrs[i].sh_info;
932e01402b1SRalf Baechle 
933e01402b1SRalf Baechle  			/* Not a valid relocation section? */
934e01402b1SRalf Baechle  			if (info >= hdr->e_shnum)
935e01402b1SRalf Baechle  				continue;
936e01402b1SRalf Baechle 
937e01402b1SRalf Baechle  			/* Don't bother with non-allocated sections */
938e01402b1SRalf Baechle  			if (!(sechdrs[info].sh_flags & SHF_ALLOC))
939e01402b1SRalf Baechle  				continue;
940e01402b1SRalf Baechle 
941e01402b1SRalf Baechle  			if (sechdrs[i].sh_type == SHT_REL)
9422600990eSRalf Baechle  				err = apply_relocations(sechdrs, strtab, symindex, i,
9432600990eSRalf Baechle  							&mod);
944e01402b1SRalf Baechle  			else if (sechdrs[i].sh_type == SHT_RELA)
945e01402b1SRalf Baechle  				err = apply_relocate_add(sechdrs, strtab, symindex, i,
946e01402b1SRalf Baechle  							 &mod);
9472600990eSRalf Baechle  			if (err < 0)
9482600990eSRalf Baechle  				return err;
9492600990eSRalf Baechle 
9502600990eSRalf Baechle   		}
9512600990eSRalf Baechle   	} else {
952bdf5d42cSRalf Baechle 		struct elf_phdr *phdr = (struct elf_phdr *) ((char *)hdr + hdr->e_phoff);
9532600990eSRalf Baechle 
954bdf5d42cSRalf Baechle 		for (i = 0; i < hdr->e_phnum; i++) {
955b618336aSKevin D. Kissell 			if (phdr->p_type == PT_LOAD) {
956b618336aSKevin D. Kissell 				memcpy((void *)phdr->p_paddr,
957b618336aSKevin D. Kissell 				       (char *)hdr + phdr->p_offset,
958b618336aSKevin D. Kissell 				       phdr->p_filesz);
959b618336aSKevin D. Kissell 				memset((void *)phdr->p_paddr + phdr->p_filesz,
960b618336aSKevin D. Kissell 				       0, phdr->p_memsz - phdr->p_filesz);
961b618336aSKevin D. Kissell 		    }
962bdf5d42cSRalf Baechle 		    phdr++;
963bdf5d42cSRalf Baechle 		}
964bdf5d42cSRalf Baechle 
965bdf5d42cSRalf Baechle 		for (i = 0; i < hdr->e_shnum; i++) {
9662600990eSRalf Baechle  			/* Internal symbols and strings. */
9672600990eSRalf Baechle  			if (sechdrs[i].sh_type == SHT_SYMTAB) {
9682600990eSRalf Baechle  				symindex = i;
9692600990eSRalf Baechle  				strindex = sechdrs[i].sh_link;
9702600990eSRalf Baechle  				strtab = (char *)hdr + sechdrs[strindex].sh_offset;
9712600990eSRalf Baechle 
9722600990eSRalf Baechle  				/* mark the symtab's address for when we try to find the
9732600990eSRalf Baechle  				   magic symbols */
9742600990eSRalf Baechle  				sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
9752600990eSRalf Baechle  			}
976e01402b1SRalf Baechle 		}
977e01402b1SRalf Baechle 	}
978e01402b1SRalf Baechle 
979e01402b1SRalf Baechle 	/* make sure it's physically written out */
980e01402b1SRalf Baechle 	flush_icache_range((unsigned long)v->load_addr,
981e01402b1SRalf Baechle 			   (unsigned long)v->load_addr + v->len);
982e01402b1SRalf Baechle 
983e01402b1SRalf Baechle 	if ((find_vpe_symbols(v, sechdrs, symindex, strtab, &mod)) < 0) {
9842600990eSRalf Baechle 		if (v->__start == 0) {
9852600990eSRalf Baechle 			printk(KERN_WARNING "VPE loader: program does not contain "
9862600990eSRalf Baechle 			       "a __start symbol\n");
9872600990eSRalf Baechle 			return -ENOEXEC;
9882600990eSRalf Baechle 		}
989e01402b1SRalf Baechle 
9902600990eSRalf Baechle 		if (v->shared_ptr == NULL)
9912600990eSRalf Baechle 			printk(KERN_WARNING "VPE loader: "
9922600990eSRalf Baechle 			       "program does not contain vpe_shared symbol.\n"
9932600990eSRalf Baechle 			       " Unable to use AMVP (AP/SP) facilities.\n");
994e01402b1SRalf Baechle 	}
995e01402b1SRalf Baechle 
996e01402b1SRalf Baechle 	printk(" elf loaded\n");
9972600990eSRalf Baechle 	return 0;
998e01402b1SRalf Baechle }
999e01402b1SRalf Baechle 
10002600990eSRalf Baechle static void cleanup_tc(struct tc *tc)
1001e01402b1SRalf Baechle {
100207cc0c9eSRalf Baechle 	unsigned long flags;
100307cc0c9eSRalf Baechle 	unsigned int mtflags, vpflags;
10042600990eSRalf Baechle 	int tmp;
1005e01402b1SRalf Baechle 
100607cc0c9eSRalf Baechle 	local_irq_save(flags);
100707cc0c9eSRalf Baechle 	mtflags = dmt();
100807cc0c9eSRalf Baechle 	vpflags = dvpe();
10092600990eSRalf Baechle 	/* Put MVPE's into 'configuration state' */
10102600990eSRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1011e01402b1SRalf Baechle 
10122600990eSRalf Baechle 	settc(tc->index);
1013e01402b1SRalf Baechle 	tmp = read_tc_c0_tcstatus();
1014e01402b1SRalf Baechle 
1015e01402b1SRalf Baechle 	/* mark not allocated and not dynamically allocatable */
1016e01402b1SRalf Baechle 	tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1017e01402b1SRalf Baechle 	tmp |= TCSTATUS_IXMT;	/* interrupt exempt */
1018e01402b1SRalf Baechle 	write_tc_c0_tcstatus(tmp);
1019e01402b1SRalf Baechle 
1020e01402b1SRalf Baechle 	write_tc_c0_tchalt(TCHALT_H);
10217c3a622dSNigel Stephens 	mips_ihb();
1022e01402b1SRalf Baechle 
10232600990eSRalf Baechle 	/* bind it to anything other than VPE1 */
102407cc0c9eSRalf Baechle //	write_tc_c0_tcbind(read_tc_c0_tcbind() & ~TCBIND_CURVPE); // | TCBIND_CURVPE
10252600990eSRalf Baechle 
10262600990eSRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
102707cc0c9eSRalf Baechle 	evpe(vpflags);
102807cc0c9eSRalf Baechle 	emt(mtflags);
102907cc0c9eSRalf Baechle 	local_irq_restore(flags);
10302600990eSRalf Baechle }
10312600990eSRalf Baechle 
10322600990eSRalf Baechle static int getcwd(char *buff, int size)
10332600990eSRalf Baechle {
10342600990eSRalf Baechle 	mm_segment_t old_fs;
10352600990eSRalf Baechle 	int ret;
10362600990eSRalf Baechle 
10372600990eSRalf Baechle 	old_fs = get_fs();
10382600990eSRalf Baechle 	set_fs(KERNEL_DS);
10392600990eSRalf Baechle 
10402600990eSRalf Baechle 	ret = sys_getcwd(buff, size);
10412600990eSRalf Baechle 
10422600990eSRalf Baechle 	set_fs(old_fs);
10432600990eSRalf Baechle 
10442600990eSRalf Baechle 	return ret;
10452600990eSRalf Baechle }
10462600990eSRalf Baechle 
10472600990eSRalf Baechle /* checks VPE is unused and gets ready to load program  */
10482600990eSRalf Baechle static int vpe_open(struct inode *inode, struct file *filp)
10492600990eSRalf Baechle {
1050c4c4018bSRalf Baechle 	enum vpe_state state;
10512600990eSRalf Baechle 	struct vpe_notifications *not;
105207cc0c9eSRalf Baechle 	struct vpe *v;
105307cc0c9eSRalf Baechle 	int ret;
10542600990eSRalf Baechle 
105507cc0c9eSRalf Baechle 	if (minor != iminor(inode)) {
105607cc0c9eSRalf Baechle 		/* assume only 1 device at the moment. */
10572600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: only vpe1 is supported\n");
10582600990eSRalf Baechle 		return -ENODEV;
10592600990eSRalf Baechle 	}
10602600990eSRalf Baechle 
106107cc0c9eSRalf Baechle 	if ((v = get_vpe(tclimit)) == NULL) {
10622600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: unable to get vpe\n");
10632600990eSRalf Baechle 		return -ENODEV;
10642600990eSRalf Baechle 	}
10652600990eSRalf Baechle 
1066c4c4018bSRalf Baechle 	state = xchg(&v->state, VPE_STATE_INUSE);
1067c4c4018bSRalf Baechle 	if (state != VPE_STATE_UNUSED) {
10682600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: tc in use dumping regs\n");
10692600990eSRalf Baechle 
10702600990eSRalf Baechle 		list_for_each_entry(not, &v->notify, list) {
107107cc0c9eSRalf Baechle 			not->stop(tclimit);
10722600990eSRalf Baechle 		}
10732600990eSRalf Baechle 
10742600990eSRalf Baechle 		release_progmem(v->load_addr);
107507cc0c9eSRalf Baechle 		cleanup_tc(get_tc(tclimit));
1076e01402b1SRalf Baechle 	}
1077e01402b1SRalf Baechle 
1078e01402b1SRalf Baechle 	/* this of-course trashes what was there before... */
1079e01402b1SRalf Baechle 	v->pbuffer = vmalloc(P_SIZE);
1080e01402b1SRalf Baechle 	v->plen = P_SIZE;
1081e01402b1SRalf Baechle 	v->load_addr = NULL;
1082e01402b1SRalf Baechle 	v->len = 0;
1083e01402b1SRalf Baechle 
10842600990eSRalf Baechle 	v->uid = filp->f_uid;
10852600990eSRalf Baechle 	v->gid = filp->f_gid;
10862600990eSRalf Baechle 
10872600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
10882600990eSRalf Baechle 	/* get kspd to tell us when a syscall_exit happens */
10892600990eSRalf Baechle 	if (!kspd_events_reqd) {
10902600990eSRalf Baechle 		kspd_notify(&kspd_events);
10912600990eSRalf Baechle 		kspd_events_reqd++;
10922600990eSRalf Baechle 	}
10932600990eSRalf Baechle #endif
10942600990eSRalf Baechle 
10952600990eSRalf Baechle 	v->cwd[0] = 0;
10962600990eSRalf Baechle 	ret = getcwd(v->cwd, VPE_PATH_MAX);
10972600990eSRalf Baechle 	if (ret < 0)
10982600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: open, getcwd returned %d\n", ret);
10992600990eSRalf Baechle 
11002600990eSRalf Baechle 	v->shared_ptr = NULL;
11012600990eSRalf Baechle 	v->__start = 0;
110207cc0c9eSRalf Baechle 
1103e01402b1SRalf Baechle 	return 0;
1104e01402b1SRalf Baechle }
1105e01402b1SRalf Baechle 
1106e01402b1SRalf Baechle static int vpe_release(struct inode *inode, struct file *filp)
1107e01402b1SRalf Baechle {
1108307bd284SRalf Baechle 	struct vpe *v;
1109e01402b1SRalf Baechle 	Elf_Ehdr *hdr;
111007cc0c9eSRalf Baechle 	int ret = 0;
1111e01402b1SRalf Baechle 
111207cc0c9eSRalf Baechle 	v = get_vpe(tclimit);
111307cc0c9eSRalf Baechle 	if (v == NULL)
1114e01402b1SRalf Baechle 		return -ENODEV;
1115e01402b1SRalf Baechle 
1116e01402b1SRalf Baechle 	hdr = (Elf_Ehdr *) v->pbuffer;
1117e01402b1SRalf Baechle 	if (memcmp(hdr->e_ident, ELFMAG, 4) == 0) {
111807cc0c9eSRalf Baechle 		if (vpe_elfload(v) >= 0) {
1119e01402b1SRalf Baechle 			vpe_run(v);
112007cc0c9eSRalf Baechle 		} else {
11212600990eSRalf Baechle  			printk(KERN_WARNING "VPE loader: ELF load failed.\n");
1122e01402b1SRalf Baechle 			ret = -ENOEXEC;
1123e01402b1SRalf Baechle 		}
1124e01402b1SRalf Baechle 	} else {
11252600990eSRalf Baechle  		printk(KERN_WARNING "VPE loader: only elf files are supported\n");
1126e01402b1SRalf Baechle 		ret = -ENOEXEC;
1127e01402b1SRalf Baechle 	}
1128e01402b1SRalf Baechle 
11292600990eSRalf Baechle 	/* It's good to be able to run the SP and if it chokes have a look at
11302600990eSRalf Baechle 	   the /dev/rt?. But if we reset the pointer to the shared struct we
11312600990eSRalf Baechle 	   loose what has happened. So perhaps if garbage is sent to the vpe
11322600990eSRalf Baechle 	   device, use it as a trigger for the reset. Hopefully a nice
11332600990eSRalf Baechle 	   executable will be along shortly. */
11342600990eSRalf Baechle 	if (ret < 0)
11352600990eSRalf Baechle 		v->shared_ptr = NULL;
11362600990eSRalf Baechle 
1137e01402b1SRalf Baechle 	// cleanup any temp buffers
1138e01402b1SRalf Baechle 	if (v->pbuffer)
1139e01402b1SRalf Baechle 		vfree(v->pbuffer);
1140e01402b1SRalf Baechle 	v->plen = 0;
1141e01402b1SRalf Baechle 	return ret;
1142e01402b1SRalf Baechle }
1143e01402b1SRalf Baechle 
1144e01402b1SRalf Baechle static ssize_t vpe_write(struct file *file, const char __user * buffer,
1145e01402b1SRalf Baechle 			 size_t count, loff_t * ppos)
1146e01402b1SRalf Baechle {
1147e01402b1SRalf Baechle 	size_t ret = count;
1148307bd284SRalf Baechle 	struct vpe *v;
1149e01402b1SRalf Baechle 
115007cc0c9eSRalf Baechle 	if (iminor(file->f_path.dentry->d_inode) != minor)
115107cc0c9eSRalf Baechle 		return -ENODEV;
115207cc0c9eSRalf Baechle 
115307cc0c9eSRalf Baechle 	v = get_vpe(tclimit);
115407cc0c9eSRalf Baechle 	if (v == NULL)
1155e01402b1SRalf Baechle 		return -ENODEV;
1156e01402b1SRalf Baechle 
1157e01402b1SRalf Baechle 	if (v->pbuffer == NULL) {
11582600990eSRalf Baechle 		printk(KERN_ERR "VPE loader: no buffer for program\n");
1159e01402b1SRalf Baechle 		return -ENOMEM;
1160e01402b1SRalf Baechle 	}
1161e01402b1SRalf Baechle 
1162e01402b1SRalf Baechle 	if ((count + v->len) > v->plen) {
1163e01402b1SRalf Baechle 		printk(KERN_WARNING
11642600990eSRalf Baechle 		       "VPE loader: elf size too big. Perhaps strip uneeded symbols\n");
1165e01402b1SRalf Baechle 		return -ENOMEM;
1166e01402b1SRalf Baechle 	}
1167e01402b1SRalf Baechle 
1168e01402b1SRalf Baechle 	count -= copy_from_user(v->pbuffer + v->len, buffer, count);
11692600990eSRalf Baechle 	if (!count)
1170e01402b1SRalf Baechle 		return -EFAULT;
1171e01402b1SRalf Baechle 
1172e01402b1SRalf Baechle 	v->len += count;
1173e01402b1SRalf Baechle 	return ret;
1174e01402b1SRalf Baechle }
1175e01402b1SRalf Baechle 
11765dfe4c96SArjan van de Ven static const struct file_operations vpe_fops = {
1177e01402b1SRalf Baechle 	.owner = THIS_MODULE,
1178e01402b1SRalf Baechle 	.open = vpe_open,
1179e01402b1SRalf Baechle 	.release = vpe_release,
1180e01402b1SRalf Baechle 	.write = vpe_write
1181e01402b1SRalf Baechle };
1182e01402b1SRalf Baechle 
1183e01402b1SRalf Baechle /* module wrapper entry points */
1184e01402b1SRalf Baechle /* give me a vpe */
1185e01402b1SRalf Baechle vpe_handle vpe_alloc(void)
1186e01402b1SRalf Baechle {
1187e01402b1SRalf Baechle 	int i;
1188e01402b1SRalf Baechle 	struct vpe *v;
1189e01402b1SRalf Baechle 
1190e01402b1SRalf Baechle 	/* find a vpe */
1191e01402b1SRalf Baechle 	for (i = 1; i < MAX_VPES; i++) {
1192e01402b1SRalf Baechle 		if ((v = get_vpe(i)) != NULL) {
1193e01402b1SRalf Baechle 			v->state = VPE_STATE_INUSE;
1194e01402b1SRalf Baechle 			return v;
1195e01402b1SRalf Baechle 		}
1196e01402b1SRalf Baechle 	}
1197e01402b1SRalf Baechle 	return NULL;
1198e01402b1SRalf Baechle }
1199e01402b1SRalf Baechle 
1200e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_alloc);
1201e01402b1SRalf Baechle 
1202e01402b1SRalf Baechle /* start running from here */
1203e01402b1SRalf Baechle int vpe_start(vpe_handle vpe, unsigned long start)
1204e01402b1SRalf Baechle {
1205e01402b1SRalf Baechle 	struct vpe *v = vpe;
1206e01402b1SRalf Baechle 
1207e01402b1SRalf Baechle 	v->__start = start;
1208e01402b1SRalf Baechle 	return vpe_run(v);
1209e01402b1SRalf Baechle }
1210e01402b1SRalf Baechle 
1211e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_start);
1212e01402b1SRalf Baechle 
1213e01402b1SRalf Baechle /* halt it for now */
1214e01402b1SRalf Baechle int vpe_stop(vpe_handle vpe)
1215e01402b1SRalf Baechle {
1216e01402b1SRalf Baechle 	struct vpe *v = vpe;
1217e01402b1SRalf Baechle 	struct tc *t;
1218e01402b1SRalf Baechle 	unsigned int evpe_flags;
1219e01402b1SRalf Baechle 
1220e01402b1SRalf Baechle 	evpe_flags = dvpe();
1221e01402b1SRalf Baechle 
1222e01402b1SRalf Baechle 	if ((t = list_entry(v->tc.next, struct tc, tc)) != NULL) {
1223e01402b1SRalf Baechle 
1224e01402b1SRalf Baechle 		settc(t->index);
1225e01402b1SRalf Baechle 		write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
1226e01402b1SRalf Baechle 	}
1227e01402b1SRalf Baechle 
1228e01402b1SRalf Baechle 	evpe(evpe_flags);
1229e01402b1SRalf Baechle 
1230e01402b1SRalf Baechle 	return 0;
1231e01402b1SRalf Baechle }
1232e01402b1SRalf Baechle 
1233e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_stop);
1234e01402b1SRalf Baechle 
1235e01402b1SRalf Baechle /* I've done with it thank you */
1236e01402b1SRalf Baechle int vpe_free(vpe_handle vpe)
1237e01402b1SRalf Baechle {
1238e01402b1SRalf Baechle 	struct vpe *v = vpe;
1239e01402b1SRalf Baechle 	struct tc *t;
1240e01402b1SRalf Baechle 	unsigned int evpe_flags;
1241e01402b1SRalf Baechle 
1242e01402b1SRalf Baechle 	if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
1243e01402b1SRalf Baechle 		return -ENOEXEC;
1244e01402b1SRalf Baechle 	}
1245e01402b1SRalf Baechle 
1246e01402b1SRalf Baechle 	evpe_flags = dvpe();
1247e01402b1SRalf Baechle 
1248e01402b1SRalf Baechle 	/* Put MVPE's into 'configuration state' */
1249340ee4b9SRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1250e01402b1SRalf Baechle 
1251e01402b1SRalf Baechle 	settc(t->index);
1252e01402b1SRalf Baechle 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
1253e01402b1SRalf Baechle 
12547c3a622dSNigel Stephens 	/* halt the TC */
1255e01402b1SRalf Baechle 	write_tc_c0_tchalt(TCHALT_H);
12567c3a622dSNigel Stephens 	mips_ihb();
12577c3a622dSNigel Stephens 
12587c3a622dSNigel Stephens 	/* mark the TC unallocated */
12597c3a622dSNigel Stephens 	write_tc_c0_tcstatus(read_tc_c0_tcstatus() & ~TCSTATUS_A);
1260e01402b1SRalf Baechle 
1261e01402b1SRalf Baechle 	v->state = VPE_STATE_UNUSED;
1262e01402b1SRalf Baechle 
1263340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
1264e01402b1SRalf Baechle 	evpe(evpe_flags);
1265e01402b1SRalf Baechle 
1266e01402b1SRalf Baechle 	return 0;
1267e01402b1SRalf Baechle }
1268e01402b1SRalf Baechle 
1269e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_free);
1270e01402b1SRalf Baechle 
1271e01402b1SRalf Baechle void *vpe_get_shared(int index)
1272e01402b1SRalf Baechle {
1273e01402b1SRalf Baechle 	struct vpe *v;
1274e01402b1SRalf Baechle 
12752600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
1276e01402b1SRalf Baechle 		return NULL;
1277e01402b1SRalf Baechle 
1278e01402b1SRalf Baechle 	return v->shared_ptr;
1279e01402b1SRalf Baechle }
1280e01402b1SRalf Baechle 
1281e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_get_shared);
1282e01402b1SRalf Baechle 
12832600990eSRalf Baechle int vpe_getuid(int index)
12842600990eSRalf Baechle {
12852600990eSRalf Baechle 	struct vpe *v;
12862600990eSRalf Baechle 
12872600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
12882600990eSRalf Baechle 		return -1;
12892600990eSRalf Baechle 
12902600990eSRalf Baechle 	return v->uid;
12912600990eSRalf Baechle }
12922600990eSRalf Baechle 
12932600990eSRalf Baechle EXPORT_SYMBOL(vpe_getuid);
12942600990eSRalf Baechle 
12952600990eSRalf Baechle int vpe_getgid(int index)
12962600990eSRalf Baechle {
12972600990eSRalf Baechle 	struct vpe *v;
12982600990eSRalf Baechle 
12992600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13002600990eSRalf Baechle 		return -1;
13012600990eSRalf Baechle 
13022600990eSRalf Baechle 	return v->gid;
13032600990eSRalf Baechle }
13042600990eSRalf Baechle 
13052600990eSRalf Baechle EXPORT_SYMBOL(vpe_getgid);
13062600990eSRalf Baechle 
13072600990eSRalf Baechle int vpe_notify(int index, struct vpe_notifications *notify)
13082600990eSRalf Baechle {
13092600990eSRalf Baechle 	struct vpe *v;
13102600990eSRalf Baechle 
13112600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13122600990eSRalf Baechle 		return -1;
13132600990eSRalf Baechle 
13142600990eSRalf Baechle 	list_add(&notify->list, &v->notify);
13152600990eSRalf Baechle 	return 0;
13162600990eSRalf Baechle }
13172600990eSRalf Baechle 
13182600990eSRalf Baechle EXPORT_SYMBOL(vpe_notify);
13192600990eSRalf Baechle 
13202600990eSRalf Baechle char *vpe_getcwd(int index)
13212600990eSRalf Baechle {
13222600990eSRalf Baechle 	struct vpe *v;
13232600990eSRalf Baechle 
13242600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13252600990eSRalf Baechle 		return NULL;
13262600990eSRalf Baechle 
13272600990eSRalf Baechle 	return v->cwd;
13282600990eSRalf Baechle }
13292600990eSRalf Baechle 
13302600990eSRalf Baechle EXPORT_SYMBOL(vpe_getcwd);
13312600990eSRalf Baechle 
13322600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
13332600990eSRalf Baechle static void kspd_sp_exit( int sp_id)
13342600990eSRalf Baechle {
13352600990eSRalf Baechle 	cleanup_tc(get_tc(sp_id));
13362600990eSRalf Baechle }
13372600990eSRalf Baechle #endif
13382600990eSRalf Baechle 
1339736fad17SKay Sievers static ssize_t store_kill(struct device *dev, struct device_attribute *attr,
1340736fad17SKay Sievers 			  const char *buf, size_t len)
13410f5d0df3SRalf Baechle {
13420f5d0df3SRalf Baechle 	struct vpe *vpe = get_vpe(tclimit);
13430f5d0df3SRalf Baechle 	struct vpe_notifications *not;
13440f5d0df3SRalf Baechle 
13450f5d0df3SRalf Baechle 	list_for_each_entry(not, &vpe->notify, list) {
13460f5d0df3SRalf Baechle 		not->stop(tclimit);
13470f5d0df3SRalf Baechle 	}
13480f5d0df3SRalf Baechle 
13490f5d0df3SRalf Baechle 	release_progmem(vpe->load_addr);
13500f5d0df3SRalf Baechle 	cleanup_tc(get_tc(tclimit));
13510f5d0df3SRalf Baechle 	vpe_stop(vpe);
13520f5d0df3SRalf Baechle 	vpe_free(vpe);
13530f5d0df3SRalf Baechle 
13540f5d0df3SRalf Baechle 	return len;
13550f5d0df3SRalf Baechle }
13560f5d0df3SRalf Baechle 
1357736fad17SKay Sievers static ssize_t show_ntcs(struct device *cd, struct device_attribute *attr,
1358736fad17SKay Sievers 			 char *buf)
135941790e04SRalf Baechle {
136041790e04SRalf Baechle 	struct vpe *vpe = get_vpe(tclimit);
136141790e04SRalf Baechle 
136241790e04SRalf Baechle 	return sprintf(buf, "%d\n", vpe->ntcs);
136341790e04SRalf Baechle }
136441790e04SRalf Baechle 
1365736fad17SKay Sievers static ssize_t store_ntcs(struct device *dev, struct device_attribute *attr,
1366736fad17SKay Sievers 			  const char *buf, size_t len)
136741790e04SRalf Baechle {
136841790e04SRalf Baechle 	struct vpe *vpe = get_vpe(tclimit);
136941790e04SRalf Baechle 	unsigned long new;
137041790e04SRalf Baechle 	char *endp;
137141790e04SRalf Baechle 
137241790e04SRalf Baechle 	new = simple_strtoul(buf, &endp, 0);
137341790e04SRalf Baechle 	if (endp == buf)
137441790e04SRalf Baechle 		goto out_einval;
137541790e04SRalf Baechle 
137641790e04SRalf Baechle 	if (new == 0 || new > (hw_tcs - tclimit))
137741790e04SRalf Baechle 		goto out_einval;
137841790e04SRalf Baechle 
137941790e04SRalf Baechle 	vpe->ntcs = new;
138041790e04SRalf Baechle 
138141790e04SRalf Baechle 	return len;
138241790e04SRalf Baechle 
138341790e04SRalf Baechle out_einval:
138441790e04SRalf Baechle 	return -EINVAL;;
138541790e04SRalf Baechle }
138641790e04SRalf Baechle 
1387736fad17SKay Sievers static struct device_attribute vpe_class_attributes[] = {
13880f5d0df3SRalf Baechle 	__ATTR(kill, S_IWUSR, NULL, store_kill),
138941790e04SRalf Baechle 	__ATTR(ntcs, S_IRUGO | S_IWUSR, show_ntcs, store_ntcs),
139041790e04SRalf Baechle 	{}
139141790e04SRalf Baechle };
139241790e04SRalf Baechle 
1393736fad17SKay Sievers static void vpe_device_release(struct device *cd)
139441790e04SRalf Baechle {
139541790e04SRalf Baechle 	kfree(cd);
139641790e04SRalf Baechle }
139741790e04SRalf Baechle 
139841790e04SRalf Baechle struct class vpe_class = {
139941790e04SRalf Baechle 	.name = "vpe",
140041790e04SRalf Baechle 	.owner = THIS_MODULE,
1401736fad17SKay Sievers 	.dev_release = vpe_device_release,
1402736fad17SKay Sievers 	.dev_attrs = vpe_class_attributes,
140341790e04SRalf Baechle };
140441790e04SRalf Baechle 
1405736fad17SKay Sievers struct device vpe_device;
140627a3bbafSRalf Baechle 
1407e01402b1SRalf Baechle static int __init vpe_module_init(void)
1408e01402b1SRalf Baechle {
140907cc0c9eSRalf Baechle 	unsigned int mtflags, vpflags;
141007cc0c9eSRalf Baechle 	unsigned long flags, val;
1411e01402b1SRalf Baechle 	struct vpe *v = NULL;
1412e01402b1SRalf Baechle 	struct tc *t;
141341790e04SRalf Baechle 	int tc, err;
1414e01402b1SRalf Baechle 
1415e01402b1SRalf Baechle 	if (!cpu_has_mipsmt) {
1416e01402b1SRalf Baechle 		printk("VPE loader: not a MIPS MT capable processor\n");
1417e01402b1SRalf Baechle 		return -ENODEV;
1418e01402b1SRalf Baechle 	}
1419e01402b1SRalf Baechle 
142007cc0c9eSRalf Baechle 	if (vpelimit == 0) {
142107cc0c9eSRalf Baechle 		printk(KERN_WARNING "No VPEs reserved for AP/SP, not "
142207cc0c9eSRalf Baechle 		       "initializing VPE loader.\nPass maxvpes=<n> argument as "
142307cc0c9eSRalf Baechle 		       "kernel argument\n");
142407cc0c9eSRalf Baechle 
142507cc0c9eSRalf Baechle 		return -ENODEV;
142607cc0c9eSRalf Baechle 	}
142707cc0c9eSRalf Baechle 
142807cc0c9eSRalf Baechle 	if (tclimit == 0) {
142907cc0c9eSRalf Baechle 		printk(KERN_WARNING "No TCs reserved for AP/SP, not "
143007cc0c9eSRalf Baechle 		       "initializing VPE loader.\nPass maxtcs=<n> argument as "
143107cc0c9eSRalf Baechle 		       "kernel argument\n");
143207cc0c9eSRalf Baechle 
143307cc0c9eSRalf Baechle 		return -ENODEV;
143407cc0c9eSRalf Baechle 	}
143507cc0c9eSRalf Baechle 
1436682e852eSAlexey Dobriyan 	major = register_chrdev(0, module_name, &vpe_fops);
1437682e852eSAlexey Dobriyan 	if (major < 0) {
1438e01402b1SRalf Baechle 		printk("VPE loader: unable to register character device\n");
1439307bd284SRalf Baechle 		return major;
1440e01402b1SRalf Baechle 	}
1441e01402b1SRalf Baechle 
144241790e04SRalf Baechle 	err = class_register(&vpe_class);
144341790e04SRalf Baechle 	if (err) {
144441790e04SRalf Baechle 		printk(KERN_ERR "vpe_class registration failed\n");
144527a3bbafSRalf Baechle 		goto out_chrdev;
144627a3bbafSRalf Baechle 	}
144741790e04SRalf Baechle 
1448736fad17SKay Sievers 	device_initialize(&vpe_device);
144941790e04SRalf Baechle 	vpe_device.class	= &vpe_class,
145041790e04SRalf Baechle 	vpe_device.parent	= NULL,
1451736fad17SKay Sievers 	strlcpy(vpe_device.bus_id, "vpe1", BUS_ID_SIZE);
145241790e04SRalf Baechle 	vpe_device.devt = MKDEV(major, minor);
1453736fad17SKay Sievers 	err = device_add(&vpe_device);
145441790e04SRalf Baechle 	if (err) {
145541790e04SRalf Baechle 		printk(KERN_ERR "Adding vpe_device failed\n");
145641790e04SRalf Baechle 		goto out_class;
145741790e04SRalf Baechle 	}
145827a3bbafSRalf Baechle 
145907cc0c9eSRalf Baechle 	local_irq_save(flags);
146007cc0c9eSRalf Baechle 	mtflags = dmt();
146107cc0c9eSRalf Baechle 	vpflags = dvpe();
1462e01402b1SRalf Baechle 
1463e01402b1SRalf Baechle 	/* Put MVPE's into 'configuration state' */
1464340ee4b9SRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1465e01402b1SRalf Baechle 
1466e01402b1SRalf Baechle 	/* dump_mtregs(); */
1467e01402b1SRalf Baechle 
1468e01402b1SRalf Baechle 	val = read_c0_mvpconf0();
146907cc0c9eSRalf Baechle 	hw_tcs = (val & MVPCONF0_PTC) + 1;
147007cc0c9eSRalf Baechle 	hw_vpes = ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1;
147107cc0c9eSRalf Baechle 
147207cc0c9eSRalf Baechle 	for (tc = tclimit; tc < hw_tcs; tc++) {
147307cc0c9eSRalf Baechle 		/*
147407cc0c9eSRalf Baechle 		 * Must re-enable multithreading temporarily or in case we
147507cc0c9eSRalf Baechle 		 * reschedule send IPIs or similar we might hang.
147607cc0c9eSRalf Baechle 		 */
147707cc0c9eSRalf Baechle 		clear_c0_mvpcontrol(MVPCONTROL_VPC);
147807cc0c9eSRalf Baechle 		evpe(vpflags);
147907cc0c9eSRalf Baechle 		emt(mtflags);
148007cc0c9eSRalf Baechle 		local_irq_restore(flags);
148107cc0c9eSRalf Baechle 		t = alloc_tc(tc);
148207cc0c9eSRalf Baechle 		if (!t) {
148307cc0c9eSRalf Baechle 			err = -ENOMEM;
148407cc0c9eSRalf Baechle 			goto out;
148507cc0c9eSRalf Baechle 		}
148607cc0c9eSRalf Baechle 
148707cc0c9eSRalf Baechle 		local_irq_save(flags);
148807cc0c9eSRalf Baechle 		mtflags = dmt();
148907cc0c9eSRalf Baechle 		vpflags = dvpe();
149007cc0c9eSRalf Baechle 		set_c0_mvpcontrol(MVPCONTROL_VPC);
1491e01402b1SRalf Baechle 
1492e01402b1SRalf Baechle 		/* VPE's */
149307cc0c9eSRalf Baechle 		if (tc < hw_tcs) {
149407cc0c9eSRalf Baechle 			settc(tc);
1495e01402b1SRalf Baechle 
149607cc0c9eSRalf Baechle 			if ((v = alloc_vpe(tc)) == NULL) {
1497e01402b1SRalf Baechle 				printk(KERN_WARNING "VPE: unable to allocate VPE\n");
149807cc0c9eSRalf Baechle 
149907cc0c9eSRalf Baechle 				goto out_reenable;
1500e01402b1SRalf Baechle 			}
1501e01402b1SRalf Baechle 
150241790e04SRalf Baechle 			v->ntcs = hw_tcs - tclimit;
150341790e04SRalf Baechle 
15042600990eSRalf Baechle 			/* add the tc to the list of this vpe's tc's. */
15052600990eSRalf Baechle 			list_add(&t->tc, &v->tc);
1506e01402b1SRalf Baechle 
1507e01402b1SRalf Baechle 			/* deactivate all but vpe0 */
150807cc0c9eSRalf Baechle 			if (tc >= tclimit) {
1509e01402b1SRalf Baechle 				unsigned long tmp = read_vpe_c0_vpeconf0();
1510e01402b1SRalf Baechle 
1511e01402b1SRalf Baechle 				tmp &= ~VPECONF0_VPA;
1512e01402b1SRalf Baechle 
1513e01402b1SRalf Baechle 				/* master VPE */
1514e01402b1SRalf Baechle 				tmp |= VPECONF0_MVP;
1515e01402b1SRalf Baechle 				write_vpe_c0_vpeconf0(tmp);
1516e01402b1SRalf Baechle 			}
1517e01402b1SRalf Baechle 
1518e01402b1SRalf Baechle 			/* disable multi-threading with TC's */
1519e01402b1SRalf Baechle 			write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE);
1520e01402b1SRalf Baechle 
152107cc0c9eSRalf Baechle 			if (tc >= vpelimit) {
15222600990eSRalf Baechle 				/*
15232600990eSRalf Baechle 				 * Set config to be the same as vpe0,
15242600990eSRalf Baechle 				 * particularly kseg0 coherency alg
15252600990eSRalf Baechle 				 */
1526e01402b1SRalf Baechle 				write_vpe_c0_config(read_c0_config());
1527e01402b1SRalf Baechle 			}
1528e01402b1SRalf Baechle 		}
1529e01402b1SRalf Baechle 
1530e01402b1SRalf Baechle 		/* TC's */
1531e01402b1SRalf Baechle 		t->pvpe = v;	/* set the parent vpe */
1532e01402b1SRalf Baechle 
153307cc0c9eSRalf Baechle 		if (tc >= tclimit) {
1534e01402b1SRalf Baechle 			unsigned long tmp;
1535e01402b1SRalf Baechle 
153607cc0c9eSRalf Baechle 			settc(tc);
1537e01402b1SRalf Baechle 
15382600990eSRalf Baechle 			/* Any TC that is bound to VPE0 gets left as is - in case
15392600990eSRalf Baechle 			   we are running SMTC on VPE0. A TC that is bound to any
15402600990eSRalf Baechle 			   other VPE gets bound to VPE0, ideally I'd like to make
15412600990eSRalf Baechle 			   it homeless but it doesn't appear to let me bind a TC
15422600990eSRalf Baechle 			   to a non-existent VPE. Which is perfectly reasonable.
15432600990eSRalf Baechle 
15442600990eSRalf Baechle 			   The (un)bound state is visible to an EJTAG probe so may
15452600990eSRalf Baechle 			   notify GDB...
15462600990eSRalf Baechle 			*/
15472600990eSRalf Baechle 
15482600990eSRalf Baechle 			if (((tmp = read_tc_c0_tcbind()) & TCBIND_CURVPE)) {
15492600990eSRalf Baechle 				/* tc is bound >vpe0 */
15502600990eSRalf Baechle 				write_tc_c0_tcbind(tmp & ~TCBIND_CURVPE);
15512600990eSRalf Baechle 
15522600990eSRalf Baechle 				t->pvpe = get_vpe(0);	/* set the parent vpe */
15532600990eSRalf Baechle 			}
1554e01402b1SRalf Baechle 
15557c3a622dSNigel Stephens 			/* halt the TC */
15567c3a622dSNigel Stephens 			write_tc_c0_tchalt(TCHALT_H);
15577c3a622dSNigel Stephens 			mips_ihb();
15587c3a622dSNigel Stephens 
1559e01402b1SRalf Baechle 			tmp = read_tc_c0_tcstatus();
1560e01402b1SRalf Baechle 
15612600990eSRalf Baechle 			/* mark not activated and not dynamically allocatable */
1562e01402b1SRalf Baechle 			tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1563e01402b1SRalf Baechle 			tmp |= TCSTATUS_IXMT;	/* interrupt exempt */
1564e01402b1SRalf Baechle 			write_tc_c0_tcstatus(tmp);
1565e01402b1SRalf Baechle 		}
1566e01402b1SRalf Baechle 	}
1567e01402b1SRalf Baechle 
156807cc0c9eSRalf Baechle out_reenable:
1569e01402b1SRalf Baechle 	/* release config state */
1570340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
1571e01402b1SRalf Baechle 
157207cc0c9eSRalf Baechle 	evpe(vpflags);
157307cc0c9eSRalf Baechle 	emt(mtflags);
157407cc0c9eSRalf Baechle 	local_irq_restore(flags);
157507cc0c9eSRalf Baechle 
15762600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
15772600990eSRalf Baechle 	kspd_events.kspd_sp_exit = kspd_sp_exit;
15782600990eSRalf Baechle #endif
1579e01402b1SRalf Baechle 	return 0;
158027a3bbafSRalf Baechle 
158141790e04SRalf Baechle out_class:
158241790e04SRalf Baechle 	class_unregister(&vpe_class);
158327a3bbafSRalf Baechle out_chrdev:
158427a3bbafSRalf Baechle 	unregister_chrdev(major, module_name);
158527a3bbafSRalf Baechle 
158607cc0c9eSRalf Baechle out:
158727a3bbafSRalf Baechle 	return err;
1588e01402b1SRalf Baechle }
1589e01402b1SRalf Baechle 
1590e01402b1SRalf Baechle static void __exit vpe_module_exit(void)
1591e01402b1SRalf Baechle {
1592e01402b1SRalf Baechle 	struct vpe *v, *n;
1593e01402b1SRalf Baechle 
1594e01402b1SRalf Baechle 	list_for_each_entry_safe(v, n, &vpecontrol.vpe_list, list) {
1595e01402b1SRalf Baechle 		if (v->state != VPE_STATE_UNUSED) {
1596e01402b1SRalf Baechle 			release_vpe(v);
1597e01402b1SRalf Baechle 		}
1598e01402b1SRalf Baechle 	}
1599e01402b1SRalf Baechle 
1600736fad17SKay Sievers 	device_del(&vpe_device);
1601e01402b1SRalf Baechle 	unregister_chrdev(major, module_name);
1602e01402b1SRalf Baechle }
1603e01402b1SRalf Baechle 
1604e01402b1SRalf Baechle module_init(vpe_module_init);
1605e01402b1SRalf Baechle module_exit(vpe_module_exit);
1606e01402b1SRalf Baechle MODULE_DESCRIPTION("MIPS VPE Loader");
16072600990eSRalf Baechle MODULE_AUTHOR("Elizabeth Oldham, MIPS Technologies, Inc.");
1608e01402b1SRalf Baechle MODULE_LICENSE("GPL");
1609