xref: /openbmc/linux/arch/mips/kernel/vpe.c (revision 60063497)
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.
2225985edcSLucas De Marchi  * The SP environment 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>
4960063497SArun Sharma #include <linux/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;
76982f6ffeSRalf Baechle static int kspd_events_reqd;
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 {
1461bbfc20dSRalf Baechle 	spinlock_t vpe_list_lock;
1471bbfc20dSRalf Baechle 	struct list_head vpe_list;	/* Virtual processing elements */
1481bbfc20dSRalf Baechle 	spinlock_t tc_list_lock;
1491bbfc20dSRalf Baechle 	struct list_head tc_list;	/* Thread contexts */
1509cfdf6f1SRalf Baechle } vpecontrol = {
15152bd080dSThomas Gleixner 	.vpe_list_lock	= __SPIN_LOCK_UNLOCKED(vpe_list_lock),
1529cfdf6f1SRalf Baechle 	.vpe_list	= LIST_HEAD_INIT(vpecontrol.vpe_list),
15352bd080dSThomas Gleixner 	.tc_list_lock	= __SPIN_LOCK_UNLOCKED(tc_list_lock),
1549cfdf6f1SRalf Baechle 	.tc_list	= LIST_HEAD_INIT(vpecontrol.tc_list)
1559cfdf6f1SRalf Baechle };
156e01402b1SRalf Baechle 
157e01402b1SRalf Baechle static void release_progmem(void *ptr);
158e01402b1SRalf Baechle 
159e01402b1SRalf Baechle /* get the vpe associated with this minor */
160f18b51ccSRalf Baechle static struct vpe *get_vpe(int minor)
161e01402b1SRalf Baechle {
1621bbfc20dSRalf Baechle 	struct vpe *res, *v;
163e01402b1SRalf Baechle 
1642600990eSRalf Baechle 	if (!cpu_has_mipsmt)
1652600990eSRalf Baechle 		return NULL;
1662600990eSRalf Baechle 
1671bbfc20dSRalf Baechle 	res = NULL;
1681bbfc20dSRalf Baechle 	spin_lock(&vpecontrol.vpe_list_lock);
169e01402b1SRalf Baechle 	list_for_each_entry(v, &vpecontrol.vpe_list, list) {
1701bbfc20dSRalf Baechle 		if (v->minor == minor) {
1711bbfc20dSRalf Baechle 			res = v;
1721bbfc20dSRalf Baechle 			break;
173e01402b1SRalf Baechle 		}
1741bbfc20dSRalf Baechle 	}
1751bbfc20dSRalf Baechle 	spin_unlock(&vpecontrol.vpe_list_lock);
176e01402b1SRalf Baechle 
1771bbfc20dSRalf Baechle 	return res;
178e01402b1SRalf Baechle }
179e01402b1SRalf Baechle 
180e01402b1SRalf Baechle /* get the vpe associated with this minor */
181f18b51ccSRalf Baechle static struct tc *get_tc(int index)
182e01402b1SRalf Baechle {
1831bbfc20dSRalf Baechle 	struct tc *res, *t;
184e01402b1SRalf Baechle 
1851bbfc20dSRalf Baechle 	res = NULL;
1861bbfc20dSRalf Baechle 	spin_lock(&vpecontrol.tc_list_lock);
187e01402b1SRalf Baechle 	list_for_each_entry(t, &vpecontrol.tc_list, list) {
1881bbfc20dSRalf Baechle 		if (t->index == index) {
1891bbfc20dSRalf Baechle 			res = t;
1901bbfc20dSRalf Baechle 			break;
191e01402b1SRalf Baechle 		}
1921bbfc20dSRalf Baechle 	}
1931bbfc20dSRalf Baechle 	spin_unlock(&vpecontrol.tc_list_lock);
194e01402b1SRalf Baechle 
195e01402b1SRalf Baechle 	return NULL;
196e01402b1SRalf Baechle }
197e01402b1SRalf Baechle 
198e01402b1SRalf Baechle /* allocate a vpe and associate it with this minor (or index) */
199f18b51ccSRalf Baechle static struct vpe *alloc_vpe(int minor)
200e01402b1SRalf Baechle {
201e01402b1SRalf Baechle 	struct vpe *v;
202e01402b1SRalf Baechle 
2031bbfc20dSRalf Baechle 	if ((v = kzalloc(sizeof(struct vpe), GFP_KERNEL)) == NULL)
204e01402b1SRalf Baechle 		return NULL;
205e01402b1SRalf Baechle 
206e01402b1SRalf Baechle 	INIT_LIST_HEAD(&v->tc);
2071bbfc20dSRalf Baechle 	spin_lock(&vpecontrol.vpe_list_lock);
208e01402b1SRalf Baechle 	list_add_tail(&v->list, &vpecontrol.vpe_list);
2091bbfc20dSRalf Baechle 	spin_unlock(&vpecontrol.vpe_list_lock);
210e01402b1SRalf Baechle 
2112600990eSRalf Baechle 	INIT_LIST_HEAD(&v->notify);
212e01402b1SRalf Baechle 	v->minor = minor;
2131bbfc20dSRalf Baechle 
214e01402b1SRalf Baechle 	return v;
215e01402b1SRalf Baechle }
216e01402b1SRalf Baechle 
217e01402b1SRalf Baechle /* allocate a tc. At startup only tc0 is running, all other can be halted. */
218f18b51ccSRalf Baechle static 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;
2271bbfc20dSRalf Baechle 
2281bbfc20dSRalf Baechle 	spin_lock(&vpecontrol.tc_list_lock);
22907cc0c9eSRalf Baechle 	list_add_tail(&tc->list, &vpecontrol.tc_list);
2301bbfc20dSRalf Baechle 	spin_unlock(&vpecontrol.tc_list_lock);
231e01402b1SRalf Baechle 
23207cc0c9eSRalf Baechle out:
23307cc0c9eSRalf Baechle 	return tc;
234e01402b1SRalf Baechle }
235e01402b1SRalf Baechle 
236e01402b1SRalf Baechle /* clean up and free everything */
237f18b51ccSRalf Baechle static void release_vpe(struct vpe *v)
238e01402b1SRalf Baechle {
239e01402b1SRalf Baechle 	list_del(&v->list);
240e01402b1SRalf Baechle 	if (v->load_addr)
241e01402b1SRalf Baechle 		release_progmem(v);
242e01402b1SRalf Baechle 	kfree(v);
243e01402b1SRalf Baechle }
244e01402b1SRalf Baechle 
2451bbfc20dSRalf Baechle static void __maybe_unused dump_mtregs(void)
246e01402b1SRalf Baechle {
247e01402b1SRalf Baechle 	unsigned long val;
248e01402b1SRalf Baechle 
249e01402b1SRalf Baechle 	val = read_c0_config3();
250e01402b1SRalf Baechle 	printk("config3 0x%lx MT %ld\n", val,
251e01402b1SRalf Baechle 	       (val & CONFIG3_MT) >> CONFIG3_MT_SHIFT);
252e01402b1SRalf Baechle 
253e01402b1SRalf Baechle 	val = read_c0_mvpcontrol();
254e01402b1SRalf Baechle 	printk("MVPControl 0x%lx, STLB %ld VPC %ld EVP %ld\n", val,
255e01402b1SRalf Baechle 	       (val & MVPCONTROL_STLB) >> MVPCONTROL_STLB_SHIFT,
256e01402b1SRalf Baechle 	       (val & MVPCONTROL_VPC) >> MVPCONTROL_VPC_SHIFT,
257e01402b1SRalf Baechle 	       (val & MVPCONTROL_EVP));
258e01402b1SRalf Baechle 
2592600990eSRalf Baechle 	val = read_c0_mvpconf0();
2602600990eSRalf Baechle 	printk("mvpconf0 0x%lx, PVPE %ld PTC %ld M %ld\n", val,
2612600990eSRalf Baechle 	       (val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT,
2622600990eSRalf Baechle 	       val & MVPCONF0_PTC, (val & MVPCONF0_M) >> MVPCONF0_M_SHIFT);
263e01402b1SRalf Baechle }
264e01402b1SRalf Baechle 
265e01402b1SRalf Baechle /* Find some VPE program space  */
266571e0bedSRalf Baechle static void *alloc_progmem(unsigned long len)
267e01402b1SRalf Baechle {
2685408c490SRalf Baechle 	void *addr;
2695408c490SRalf Baechle 
270e01402b1SRalf Baechle #ifdef CONFIG_MIPS_VPE_LOADER_TOM
2715408c490SRalf Baechle 	/*
2725408c490SRalf Baechle 	 * This means you must tell Linux to use less memory than you
2735408c490SRalf Baechle 	 * physically have, for example by passing a mem= boot argument.
2745408c490SRalf Baechle 	 */
2759f2546adSRalf Baechle 	addr = pfn_to_kaddr(max_low_pfn);
2765408c490SRalf Baechle 	memset(addr, 0, len);
277e01402b1SRalf Baechle #else
2785408c490SRalf Baechle 	/* simple grab some mem for now */
2795408c490SRalf Baechle 	addr = kzalloc(len, GFP_KERNEL);
280e01402b1SRalf Baechle #endif
2815408c490SRalf Baechle 
2825408c490SRalf Baechle 	return addr;
283e01402b1SRalf Baechle }
284e01402b1SRalf Baechle 
285e01402b1SRalf Baechle static void release_progmem(void *ptr)
286e01402b1SRalf Baechle {
287e01402b1SRalf Baechle #ifndef CONFIG_MIPS_VPE_LOADER_TOM
288e01402b1SRalf Baechle 	kfree(ptr);
289e01402b1SRalf Baechle #endif
290e01402b1SRalf Baechle }
291e01402b1SRalf Baechle 
292e01402b1SRalf Baechle /* Update size with this section: return offset. */
293e01402b1SRalf Baechle static long get_offset(unsigned long *size, Elf_Shdr * sechdr)
294e01402b1SRalf Baechle {
295e01402b1SRalf Baechle 	long ret;
296e01402b1SRalf Baechle 
297e01402b1SRalf Baechle 	ret = ALIGN(*size, sechdr->sh_addralign ? : 1);
298e01402b1SRalf Baechle 	*size = ret + sechdr->sh_size;
299e01402b1SRalf Baechle 	return ret;
300e01402b1SRalf Baechle }
301e01402b1SRalf Baechle 
302e01402b1SRalf Baechle /* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld
303e01402b1SRalf Baechle    might -- code, read-only data, read-write data, small data.  Tally
304e01402b1SRalf Baechle    sizes, and place the offsets into sh_entsize fields: high bit means it
305e01402b1SRalf Baechle    belongs in init. */
306e01402b1SRalf Baechle static void layout_sections(struct module *mod, const Elf_Ehdr * hdr,
307e01402b1SRalf Baechle 			    Elf_Shdr * sechdrs, const char *secstrings)
308e01402b1SRalf Baechle {
309e01402b1SRalf Baechle 	static unsigned long const masks[][2] = {
310e01402b1SRalf Baechle 		/* NOTE: all executable code must be the first section
311e01402b1SRalf Baechle 		 * in this array; otherwise modify the text_size
312e01402b1SRalf Baechle 		 * finder in the two loops below */
313e01402b1SRalf Baechle 		{SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL},
314e01402b1SRalf Baechle 		{SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL},
315e01402b1SRalf Baechle 		{SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL},
316e01402b1SRalf Baechle 		{ARCH_SHF_SMALL | SHF_ALLOC, 0}
317e01402b1SRalf Baechle 	};
318e01402b1SRalf Baechle 	unsigned int m, i;
319e01402b1SRalf Baechle 
320e01402b1SRalf Baechle 	for (i = 0; i < hdr->e_shnum; i++)
321e01402b1SRalf Baechle 		sechdrs[i].sh_entsize = ~0UL;
322e01402b1SRalf Baechle 
323e01402b1SRalf Baechle 	for (m = 0; m < ARRAY_SIZE(masks); ++m) {
324e01402b1SRalf Baechle 		for (i = 0; i < hdr->e_shnum; ++i) {
325e01402b1SRalf Baechle 			Elf_Shdr *s = &sechdrs[i];
326e01402b1SRalf Baechle 
327e01402b1SRalf Baechle 			//  || strncmp(secstrings + s->sh_name, ".init", 5) == 0)
328e01402b1SRalf Baechle 			if ((s->sh_flags & masks[m][0]) != masks[m][0]
329e01402b1SRalf Baechle 			    || (s->sh_flags & masks[m][1])
330e01402b1SRalf Baechle 			    || s->sh_entsize != ~0UL)
331e01402b1SRalf Baechle 				continue;
332e2a9cf96SRaghu Gandham 			s->sh_entsize =
333e2a9cf96SRaghu Gandham 				get_offset((unsigned long *)&mod->core_size, s);
334e01402b1SRalf Baechle 		}
335e01402b1SRalf Baechle 
336e01402b1SRalf Baechle 		if (m == 0)
337e01402b1SRalf Baechle 			mod->core_text_size = mod->core_size;
338e01402b1SRalf Baechle 
339e01402b1SRalf Baechle 	}
340e01402b1SRalf Baechle }
341e01402b1SRalf Baechle 
342e01402b1SRalf Baechle 
343e01402b1SRalf Baechle /* from module-elf32.c, but subverted a little */
344e01402b1SRalf Baechle 
345e01402b1SRalf Baechle struct mips_hi16 {
346e01402b1SRalf Baechle 	struct mips_hi16 *next;
347e01402b1SRalf Baechle 	Elf32_Addr *addr;
348e01402b1SRalf Baechle 	Elf32_Addr value;
349e01402b1SRalf Baechle };
350e01402b1SRalf Baechle 
351e01402b1SRalf Baechle static struct mips_hi16 *mips_hi16_list;
352e01402b1SRalf Baechle static unsigned int gp_offs, gp_addr;
353e01402b1SRalf Baechle 
354e01402b1SRalf Baechle static int apply_r_mips_none(struct module *me, uint32_t *location,
355e01402b1SRalf Baechle 			     Elf32_Addr v)
356e01402b1SRalf Baechle {
357e01402b1SRalf Baechle 	return 0;
358e01402b1SRalf Baechle }
359e01402b1SRalf Baechle 
360e01402b1SRalf Baechle static int apply_r_mips_gprel16(struct module *me, uint32_t *location,
361e01402b1SRalf Baechle 				Elf32_Addr v)
362e01402b1SRalf Baechle {
363e01402b1SRalf Baechle 	int rel;
364e01402b1SRalf Baechle 
365e01402b1SRalf Baechle 	if( !(*location & 0xffff) ) {
366e01402b1SRalf Baechle 		rel = (int)v - gp_addr;
367e01402b1SRalf Baechle 	}
368e01402b1SRalf Baechle 	else {
369e01402b1SRalf Baechle 		/* .sbss + gp(relative) + offset */
370e01402b1SRalf Baechle 		/* kludge! */
371e01402b1SRalf Baechle 		rel =  (int)(short)((int)v + gp_offs +
372e01402b1SRalf Baechle 				    (int)(short)(*location & 0xffff) - gp_addr);
373e01402b1SRalf Baechle 	}
374e01402b1SRalf Baechle 
375e01402b1SRalf Baechle 	if( (rel > 32768) || (rel < -32768) ) {
3762600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: apply_r_mips_gprel16: "
3772600990eSRalf Baechle 		       "relative address 0x%x out of range of gp register\n",
3782600990eSRalf Baechle 		       rel);
379e01402b1SRalf Baechle 		return -ENOEXEC;
380e01402b1SRalf Baechle 	}
381e01402b1SRalf Baechle 
382e01402b1SRalf Baechle 	*location = (*location & 0xffff0000) | (rel & 0xffff);
383e01402b1SRalf Baechle 
384e01402b1SRalf Baechle 	return 0;
385e01402b1SRalf Baechle }
386e01402b1SRalf Baechle 
387e01402b1SRalf Baechle static int apply_r_mips_pc16(struct module *me, uint32_t *location,
388e01402b1SRalf Baechle 			     Elf32_Addr v)
389e01402b1SRalf Baechle {
390e01402b1SRalf Baechle 	int rel;
391e01402b1SRalf Baechle 	rel = (((unsigned int)v - (unsigned int)location));
392e01402b1SRalf Baechle 	rel >>= 2;		// because the offset is in _instructions_ not bytes.
393e01402b1SRalf Baechle 	rel -= 1;		// and one instruction less due to the branch delay slot.
394e01402b1SRalf Baechle 
395e01402b1SRalf Baechle 	if( (rel > 32768) || (rel < -32768) ) {
3962600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: "
397e01402b1SRalf Baechle  		       "apply_r_mips_pc16: relative address out of range 0x%x\n", rel);
398e01402b1SRalf Baechle 		return -ENOEXEC;
399e01402b1SRalf Baechle 	}
400e01402b1SRalf Baechle 
401e01402b1SRalf Baechle 	*location = (*location & 0xffff0000) | (rel & 0xffff);
402e01402b1SRalf Baechle 
403e01402b1SRalf Baechle 	return 0;
404e01402b1SRalf Baechle }
405e01402b1SRalf Baechle 
406e01402b1SRalf Baechle static int apply_r_mips_32(struct module *me, uint32_t *location,
407e01402b1SRalf Baechle 			   Elf32_Addr v)
408e01402b1SRalf Baechle {
409e01402b1SRalf Baechle 	*location += v;
410e01402b1SRalf Baechle 
411e01402b1SRalf Baechle 	return 0;
412e01402b1SRalf Baechle }
413e01402b1SRalf Baechle 
414e01402b1SRalf Baechle static int apply_r_mips_26(struct module *me, uint32_t *location,
415e01402b1SRalf Baechle 			   Elf32_Addr v)
416e01402b1SRalf Baechle {
417e01402b1SRalf Baechle 	if (v % 4) {
4182600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: apply_r_mips_26 "
4192600990eSRalf Baechle 		       " unaligned relocation\n");
420e01402b1SRalf Baechle 		return -ENOEXEC;
421e01402b1SRalf Baechle 	}
422e01402b1SRalf Baechle 
423307bd284SRalf Baechle /*
424307bd284SRalf Baechle  * Not desperately convinced this is a good check of an overflow condition
425307bd284SRalf Baechle  * anyway. But it gets in the way of handling undefined weak symbols which
426307bd284SRalf Baechle  * we want to set to zero.
427307bd284SRalf Baechle  * if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) {
428307bd284SRalf Baechle  * printk(KERN_ERR
429307bd284SRalf Baechle  * "module %s: relocation overflow\n",
430307bd284SRalf Baechle  * me->name);
431307bd284SRalf Baechle  * return -ENOEXEC;
432307bd284SRalf Baechle  * }
433e01402b1SRalf Baechle  */
434e01402b1SRalf Baechle 
435e01402b1SRalf Baechle 	*location = (*location & ~0x03ffffff) |
436e01402b1SRalf Baechle 		((*location + (v >> 2)) & 0x03ffffff);
437e01402b1SRalf Baechle 	return 0;
438e01402b1SRalf Baechle }
439e01402b1SRalf Baechle 
440e01402b1SRalf Baechle static int apply_r_mips_hi16(struct module *me, uint32_t *location,
441e01402b1SRalf Baechle 			     Elf32_Addr v)
442e01402b1SRalf Baechle {
443e01402b1SRalf Baechle 	struct mips_hi16 *n;
444e01402b1SRalf Baechle 
445e01402b1SRalf Baechle 	/*
446e01402b1SRalf Baechle 	 * We cannot relocate this one now because we don't know the value of
447e01402b1SRalf Baechle 	 * the carry we need to add.  Save the information, and let LO16 do the
448e01402b1SRalf Baechle 	 * actual relocation.
449e01402b1SRalf Baechle 	 */
450e01402b1SRalf Baechle 	n = kmalloc(sizeof *n, GFP_KERNEL);
451e01402b1SRalf Baechle 	if (!n)
452e01402b1SRalf Baechle 		return -ENOMEM;
453e01402b1SRalf Baechle 
454e01402b1SRalf Baechle 	n->addr = location;
455e01402b1SRalf Baechle 	n->value = v;
456e01402b1SRalf Baechle 	n->next = mips_hi16_list;
457e01402b1SRalf Baechle 	mips_hi16_list = n;
458e01402b1SRalf Baechle 
459e01402b1SRalf Baechle 	return 0;
460e01402b1SRalf Baechle }
461e01402b1SRalf Baechle 
462e01402b1SRalf Baechle static int apply_r_mips_lo16(struct module *me, uint32_t *location,
463e01402b1SRalf Baechle 			     Elf32_Addr v)
464e01402b1SRalf Baechle {
465e01402b1SRalf Baechle 	unsigned long insnlo = *location;
466e01402b1SRalf Baechle 	Elf32_Addr val, vallo;
467477c4b07SRalf Baechle 	struct mips_hi16 *l, *next;
468e01402b1SRalf Baechle 
469e01402b1SRalf Baechle 	/* Sign extend the addend we extract from the lo insn.  */
470e01402b1SRalf Baechle 	vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;
471e01402b1SRalf Baechle 
472e01402b1SRalf Baechle 	if (mips_hi16_list != NULL) {
473e01402b1SRalf Baechle 
474e01402b1SRalf Baechle 		l = mips_hi16_list;
475e01402b1SRalf Baechle 		while (l != NULL) {
476e01402b1SRalf Baechle 			unsigned long insn;
477e01402b1SRalf Baechle 
478e01402b1SRalf Baechle 			/*
479e01402b1SRalf Baechle 			 * The value for the HI16 had best be the same.
480e01402b1SRalf Baechle 			 */
481e01402b1SRalf Baechle  			if (v != l->value) {
4822600990eSRalf Baechle 				printk(KERN_DEBUG "VPE loader: "
483b1e3afa0SJoe Perches 				       "apply_r_mips_lo16/hi16: \t"
4842600990eSRalf Baechle 				       "inconsistent value information\n");
485477c4b07SRalf Baechle 				goto out_free;
486e01402b1SRalf Baechle 			}
487e01402b1SRalf Baechle 
488e01402b1SRalf Baechle 			/*
489e01402b1SRalf Baechle 			 * Do the HI16 relocation.  Note that we actually don't
490e01402b1SRalf Baechle 			 * need to know anything about the LO16 itself, except
491e01402b1SRalf Baechle 			 * where to find the low 16 bits of the addend needed
492e01402b1SRalf Baechle 			 * by the LO16.
493e01402b1SRalf Baechle 			 */
494e01402b1SRalf Baechle 			insn = *l->addr;
495e01402b1SRalf Baechle 			val = ((insn & 0xffff) << 16) + vallo;
496e01402b1SRalf Baechle 			val += v;
497e01402b1SRalf Baechle 
498e01402b1SRalf Baechle 			/*
499e01402b1SRalf Baechle 			 * Account for the sign extension that will happen in
500e01402b1SRalf Baechle 			 * the low bits.
501e01402b1SRalf Baechle 			 */
502e01402b1SRalf Baechle 			val = ((val >> 16) + ((val & 0x8000) != 0)) & 0xffff;
503e01402b1SRalf Baechle 
504e01402b1SRalf Baechle 			insn = (insn & ~0xffff) | val;
505e01402b1SRalf Baechle 			*l->addr = insn;
506e01402b1SRalf Baechle 
507e01402b1SRalf Baechle 			next = l->next;
508e01402b1SRalf Baechle 			kfree(l);
509e01402b1SRalf Baechle 			l = next;
510e01402b1SRalf Baechle 		}
511e01402b1SRalf Baechle 
512e01402b1SRalf Baechle 		mips_hi16_list = NULL;
513e01402b1SRalf Baechle 	}
514e01402b1SRalf Baechle 
515e01402b1SRalf Baechle 	/*
516e01402b1SRalf Baechle 	 * Ok, we're done with the HI16 relocs.  Now deal with the LO16.
517e01402b1SRalf Baechle 	 */
518e01402b1SRalf Baechle 	val = v + vallo;
519e01402b1SRalf Baechle 	insnlo = (insnlo & ~0xffff) | (val & 0xffff);
520e01402b1SRalf Baechle 	*location = insnlo;
521e01402b1SRalf Baechle 
522e01402b1SRalf Baechle 	return 0;
523477c4b07SRalf Baechle 
524477c4b07SRalf Baechle out_free:
525477c4b07SRalf Baechle 	while (l != NULL) {
526477c4b07SRalf Baechle 		next = l->next;
527477c4b07SRalf Baechle 		kfree(l);
528477c4b07SRalf Baechle 		l = next;
529477c4b07SRalf Baechle 	}
530477c4b07SRalf Baechle 	mips_hi16_list = NULL;
531477c4b07SRalf Baechle 
532477c4b07SRalf Baechle 	return -ENOEXEC;
533e01402b1SRalf Baechle }
534e01402b1SRalf Baechle 
535e01402b1SRalf Baechle static int (*reloc_handlers[]) (struct module *me, uint32_t *location,
536e01402b1SRalf Baechle 				Elf32_Addr v) = {
537e01402b1SRalf Baechle 	[R_MIPS_NONE]	= apply_r_mips_none,
538e01402b1SRalf Baechle 	[R_MIPS_32]	= apply_r_mips_32,
539e01402b1SRalf Baechle 	[R_MIPS_26]	= apply_r_mips_26,
540e01402b1SRalf Baechle 	[R_MIPS_HI16]	= apply_r_mips_hi16,
541e01402b1SRalf Baechle 	[R_MIPS_LO16]	= apply_r_mips_lo16,
542e01402b1SRalf Baechle 	[R_MIPS_GPREL16] = apply_r_mips_gprel16,
543e01402b1SRalf Baechle 	[R_MIPS_PC16] = apply_r_mips_pc16
544e01402b1SRalf Baechle };
545e01402b1SRalf Baechle 
5462600990eSRalf Baechle static char *rstrs[] = {
5472600990eSRalf Baechle 	[R_MIPS_NONE]	= "MIPS_NONE",
5482600990eSRalf Baechle 	[R_MIPS_32]	= "MIPS_32",
5492600990eSRalf Baechle 	[R_MIPS_26]	= "MIPS_26",
5502600990eSRalf Baechle 	[R_MIPS_HI16]	= "MIPS_HI16",
5512600990eSRalf Baechle 	[R_MIPS_LO16]	= "MIPS_LO16",
5522600990eSRalf Baechle 	[R_MIPS_GPREL16] = "MIPS_GPREL16",
5532600990eSRalf Baechle 	[R_MIPS_PC16] = "MIPS_PC16"
5542600990eSRalf Baechle };
555e01402b1SRalf Baechle 
556f18b51ccSRalf Baechle static int apply_relocations(Elf32_Shdr *sechdrs,
557e01402b1SRalf Baechle 		      const char *strtab,
558e01402b1SRalf Baechle 		      unsigned int symindex,
559e01402b1SRalf Baechle 		      unsigned int relsec,
560e01402b1SRalf Baechle 		      struct module *me)
561e01402b1SRalf Baechle {
562e01402b1SRalf Baechle 	Elf32_Rel *rel = (void *) sechdrs[relsec].sh_addr;
563e01402b1SRalf Baechle 	Elf32_Sym *sym;
564e01402b1SRalf Baechle 	uint32_t *location;
565e01402b1SRalf Baechle 	unsigned int i;
566e01402b1SRalf Baechle 	Elf32_Addr v;
567e01402b1SRalf Baechle 	int res;
568e01402b1SRalf Baechle 
569e01402b1SRalf Baechle 	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
570e01402b1SRalf Baechle 		Elf32_Word r_info = rel[i].r_info;
571e01402b1SRalf Baechle 
572e01402b1SRalf Baechle 		/* This is where to make the change */
573e01402b1SRalf Baechle 		location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
574e01402b1SRalf Baechle 			+ rel[i].r_offset;
575e01402b1SRalf Baechle 		/* This is the symbol it is referring to */
576e01402b1SRalf Baechle 		sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
577e01402b1SRalf Baechle 			+ ELF32_R_SYM(r_info);
578e01402b1SRalf Baechle 
579e01402b1SRalf Baechle 		if (!sym->st_value) {
580e01402b1SRalf Baechle 			printk(KERN_DEBUG "%s: undefined weak symbol %s\n",
581e01402b1SRalf Baechle 			       me->name, strtab + sym->st_name);
582e01402b1SRalf Baechle 			/* just print the warning, dont barf */
583e01402b1SRalf Baechle 		}
584e01402b1SRalf Baechle 
585e01402b1SRalf Baechle 		v = sym->st_value;
586e01402b1SRalf Baechle 
587e01402b1SRalf Baechle 		res = reloc_handlers[ELF32_R_TYPE(r_info)](me, location, v);
588e01402b1SRalf Baechle 		if( res ) {
5892600990eSRalf Baechle 			char *r = rstrs[ELF32_R_TYPE(r_info)];
5902600990eSRalf Baechle 		    	printk(KERN_WARNING "VPE loader: .text+0x%x "
5912600990eSRalf Baechle 			       "relocation type %s for symbol \"%s\" failed\n",
5922600990eSRalf Baechle 			       rel[i].r_offset, r ? r : "UNKNOWN",
5932600990eSRalf Baechle 			       strtab + sym->st_name);
594e01402b1SRalf Baechle 			return res;
595e01402b1SRalf Baechle 		}
5962600990eSRalf Baechle 	}
597e01402b1SRalf Baechle 
598e01402b1SRalf Baechle 	return 0;
599e01402b1SRalf Baechle }
600e01402b1SRalf Baechle 
601f18b51ccSRalf Baechle static inline void save_gp_address(unsigned int secbase, unsigned int rel)
602e01402b1SRalf Baechle {
603e01402b1SRalf Baechle 	gp_addr = secbase + rel;
604e01402b1SRalf Baechle 	gp_offs = gp_addr - (secbase & 0xffff0000);
605e01402b1SRalf Baechle }
606e01402b1SRalf Baechle /* end module-elf32.c */
607e01402b1SRalf Baechle 
608e01402b1SRalf Baechle 
609e01402b1SRalf Baechle 
610e01402b1SRalf Baechle /* Change all symbols so that sh_value encodes the pointer directly. */
6112600990eSRalf Baechle static void simplify_symbols(Elf_Shdr * sechdrs,
612e01402b1SRalf Baechle 			    unsigned int symindex,
613e01402b1SRalf Baechle 			    const char *strtab,
614e01402b1SRalf Baechle 			    const char *secstrings,
615e01402b1SRalf Baechle 			    unsigned int nsecs, struct module *mod)
616e01402b1SRalf Baechle {
617e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
618e01402b1SRalf Baechle 	unsigned long secbase, bssbase = 0;
619e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
6202600990eSRalf Baechle 	int size;
621e01402b1SRalf Baechle 
622e01402b1SRalf Baechle 	/* find the .bss section for COMMON symbols */
623e01402b1SRalf Baechle 	for (i = 0; i < nsecs; i++) {
6242600990eSRalf Baechle 		if (strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) == 0) {
625e01402b1SRalf Baechle 			bssbase = sechdrs[i].sh_addr;
6262600990eSRalf Baechle 			break;
6272600990eSRalf Baechle 		}
628e01402b1SRalf Baechle 	}
629e01402b1SRalf Baechle 
630e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
631e01402b1SRalf Baechle 		switch (sym[i].st_shndx) {
632e01402b1SRalf Baechle 		case SHN_COMMON:
6332600990eSRalf Baechle 			/* Allocate space for the symbol in the .bss section.
6342600990eSRalf Baechle 			   st_value is currently size.
635e01402b1SRalf Baechle 			   We want it to have the address of the symbol. */
636e01402b1SRalf Baechle 
637e01402b1SRalf Baechle 			size = sym[i].st_value;
638e01402b1SRalf Baechle 			sym[i].st_value = bssbase;
639e01402b1SRalf Baechle 
640e01402b1SRalf Baechle 			bssbase += size;
641e01402b1SRalf Baechle 			break;
642e01402b1SRalf Baechle 
643e01402b1SRalf Baechle 		case SHN_ABS:
644e01402b1SRalf Baechle 			/* Don't need to do anything */
645e01402b1SRalf Baechle 			break;
646e01402b1SRalf Baechle 
647e01402b1SRalf Baechle 		case SHN_UNDEF:
648e01402b1SRalf Baechle 			/* ret = -ENOENT; */
649e01402b1SRalf Baechle 			break;
650e01402b1SRalf Baechle 
651e01402b1SRalf Baechle 		case SHN_MIPS_SCOMMON:
6522600990eSRalf Baechle 			printk(KERN_DEBUG "simplify_symbols: ignoring SHN_MIPS_SCOMMON "
6532600990eSRalf Baechle 			       "symbol <%s> st_shndx %d\n", strtab + sym[i].st_name,
6542600990eSRalf Baechle 			       sym[i].st_shndx);
655e01402b1SRalf Baechle 			// .sbss section
656e01402b1SRalf Baechle 			break;
657e01402b1SRalf Baechle 
658e01402b1SRalf Baechle 		default:
659e01402b1SRalf Baechle 			secbase = sechdrs[sym[i].st_shndx].sh_addr;
660e01402b1SRalf Baechle 
661e01402b1SRalf Baechle 			if (strncmp(strtab + sym[i].st_name, "_gp", 3) == 0) {
662e01402b1SRalf Baechle 				save_gp_address(secbase, sym[i].st_value);
663e01402b1SRalf Baechle 			}
664e01402b1SRalf Baechle 
665e01402b1SRalf Baechle 			sym[i].st_value += secbase;
666e01402b1SRalf Baechle 			break;
667e01402b1SRalf Baechle 		}
668e01402b1SRalf Baechle 	}
669e01402b1SRalf Baechle }
670e01402b1SRalf Baechle 
671e01402b1SRalf Baechle #ifdef DEBUG_ELFLOADER
672e01402b1SRalf Baechle static void dump_elfsymbols(Elf_Shdr * sechdrs, unsigned int symindex,
673e01402b1SRalf Baechle 			    const char *strtab, struct module *mod)
674e01402b1SRalf Baechle {
675e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
676e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
677e01402b1SRalf Baechle 
678e01402b1SRalf Baechle 	printk(KERN_DEBUG "dump_elfsymbols: n %d\n", n);
679e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
680e01402b1SRalf Baechle 		printk(KERN_DEBUG " i %d name <%s> 0x%x\n", i,
681e01402b1SRalf Baechle 		       strtab + sym[i].st_name, sym[i].st_value);
682e01402b1SRalf Baechle 	}
683e01402b1SRalf Baechle }
684e01402b1SRalf Baechle #endif
685e01402b1SRalf Baechle 
686e01402b1SRalf Baechle /* We are prepared so configure and start the VPE... */
687be6e1437SRalf Baechle static int vpe_run(struct vpe * v)
688e01402b1SRalf Baechle {
68907cc0c9eSRalf Baechle 	unsigned long flags, val, dmt_flag;
6902600990eSRalf Baechle 	struct vpe_notifications *n;
69107cc0c9eSRalf Baechle 	unsigned int vpeflags;
692e01402b1SRalf Baechle 	struct tc *t;
693e01402b1SRalf Baechle 
694e01402b1SRalf Baechle 	/* check we are the Master VPE */
69507cc0c9eSRalf Baechle 	local_irq_save(flags);
696e01402b1SRalf Baechle 	val = read_c0_vpeconf0();
697e01402b1SRalf Baechle 	if (!(val & VPECONF0_MVP)) {
698e01402b1SRalf Baechle 		printk(KERN_WARNING
6992600990eSRalf Baechle 		       "VPE loader: only Master VPE's are allowed to configure MT\n");
70007cc0c9eSRalf Baechle 		local_irq_restore(flags);
70107cc0c9eSRalf Baechle 
702e01402b1SRalf Baechle 		return -1;
703e01402b1SRalf Baechle 	}
704e01402b1SRalf Baechle 
70507cc0c9eSRalf Baechle 	dmt_flag = dmt();
70607cc0c9eSRalf Baechle 	vpeflags = dvpe();
707e01402b1SRalf Baechle 
708e01402b1SRalf Baechle 	if (!list_empty(&v->tc)) {
709e01402b1SRalf Baechle 		if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
71007cc0c9eSRalf Baechle 			evpe(vpeflags);
71107cc0c9eSRalf Baechle 			emt(dmt_flag);
71207cc0c9eSRalf Baechle 			local_irq_restore(flags);
71307cc0c9eSRalf Baechle 
71407cc0c9eSRalf Baechle 			printk(KERN_WARNING
71507cc0c9eSRalf Baechle 			       "VPE loader: TC %d is already in use.\n",
716e01402b1SRalf Baechle                                t->index);
717e01402b1SRalf Baechle 			return -ENOEXEC;
718e01402b1SRalf Baechle 		}
719e01402b1SRalf Baechle 	} else {
72007cc0c9eSRalf Baechle 		evpe(vpeflags);
72107cc0c9eSRalf Baechle 		emt(dmt_flag);
72207cc0c9eSRalf Baechle 		local_irq_restore(flags);
72307cc0c9eSRalf Baechle 
72407cc0c9eSRalf Baechle 		printk(KERN_WARNING
72507cc0c9eSRalf Baechle 		       "VPE loader: No TC's associated with VPE %d\n",
726e01402b1SRalf Baechle 		       v->minor);
72707cc0c9eSRalf Baechle 
728e01402b1SRalf Baechle 		return -ENOEXEC;
729e01402b1SRalf Baechle 	}
730e01402b1SRalf Baechle 
7312600990eSRalf Baechle 	/* Put MVPE's into 'configuration state' */
7322600990eSRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
733e01402b1SRalf Baechle 
7342600990eSRalf Baechle 	settc(t->index);
735e01402b1SRalf Baechle 
736e01402b1SRalf Baechle 	/* should check it is halted, and not activated */
737e01402b1SRalf Baechle 	if ((read_tc_c0_tcstatus() & TCSTATUS_A) || !(read_tc_c0_tchalt() & TCHALT_H)) {
73807cc0c9eSRalf Baechle 		evpe(vpeflags);
73907cc0c9eSRalf Baechle 		emt(dmt_flag);
74007cc0c9eSRalf Baechle 		local_irq_restore(flags);
74107cc0c9eSRalf Baechle 
74207cc0c9eSRalf Baechle 		printk(KERN_WARNING "VPE loader: TC %d is already active!\n",
743e01402b1SRalf Baechle 		       t->index);
74407cc0c9eSRalf Baechle 
745e01402b1SRalf Baechle 		return -ENOEXEC;
746e01402b1SRalf Baechle 	}
747e01402b1SRalf Baechle 
748e01402b1SRalf Baechle 	/* Write the address we want it to start running from in the TCPC register. */
749e01402b1SRalf Baechle 	write_tc_c0_tcrestart((unsigned long)v->__start);
750e01402b1SRalf Baechle 	write_tc_c0_tccontext((unsigned long)0);
75107cc0c9eSRalf Baechle 
7522600990eSRalf Baechle 	/*
7532600990eSRalf Baechle 	 * Mark the TC as activated, not interrupt exempt and not dynamically
7542600990eSRalf Baechle 	 * allocatable
7552600990eSRalf Baechle 	 */
756e01402b1SRalf Baechle 	val = read_tc_c0_tcstatus();
757e01402b1SRalf Baechle 	val = (val & ~(TCSTATUS_DA | TCSTATUS_IXMT)) | TCSTATUS_A;
758e01402b1SRalf Baechle 	write_tc_c0_tcstatus(val);
759e01402b1SRalf Baechle 
760e01402b1SRalf Baechle 	write_tc_c0_tchalt(read_tc_c0_tchalt() & ~TCHALT_H);
761e01402b1SRalf Baechle 
762e01402b1SRalf Baechle 	/*
763e01402b1SRalf Baechle 	 * The sde-kit passes 'memsize' to __start in $a3, so set something
7642600990eSRalf Baechle 	 * here...  Or set $a3 to zero and define DFLT_STACK_SIZE and
765e01402b1SRalf Baechle 	 * DFLT_HEAP_SIZE when you compile your program
766e01402b1SRalf Baechle 	 */
76741790e04SRalf Baechle 	mttgpr(6, v->ntcs);
7682600990eSRalf Baechle 	mttgpr(7, physical_memsize);
769e01402b1SRalf Baechle 
7702600990eSRalf Baechle 	/* set up VPE1 */
7712600990eSRalf Baechle 	/*
7722600990eSRalf Baechle 	 * bind the TC to VPE 1 as late as possible so we only have the final
7732600990eSRalf Baechle 	 * VPE registers to set up, and so an EJTAG probe can trigger on it
7742600990eSRalf Baechle 	 */
77507cc0c9eSRalf Baechle 	write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | 1);
7762600990eSRalf Baechle 
777a94d7020SElizabeth Oldham 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~(VPECONF0_VPA));
778a94d7020SElizabeth Oldham 
779a94d7020SElizabeth Oldham 	back_to_back_c0_hazard();
780a94d7020SElizabeth Oldham 
7812600990eSRalf Baechle 	/* Set up the XTC bit in vpeconf0 to point at our tc */
7822600990eSRalf Baechle 	write_vpe_c0_vpeconf0( (read_vpe_c0_vpeconf0() & ~(VPECONF0_XTC))
7832600990eSRalf Baechle 	                      | (t->index << VPECONF0_XTC_SHIFT));
7842600990eSRalf Baechle 
785a94d7020SElizabeth Oldham 	back_to_back_c0_hazard();
786a94d7020SElizabeth Oldham 
7872600990eSRalf Baechle 	/* enable this VPE */
7882600990eSRalf Baechle 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA);
789e01402b1SRalf Baechle 
790e01402b1SRalf Baechle 	/* clear out any left overs from a previous program */
7912600990eSRalf Baechle 	write_vpe_c0_status(0);
792e01402b1SRalf Baechle 	write_vpe_c0_cause(0);
793e01402b1SRalf Baechle 
794e01402b1SRalf Baechle 	/* take system out of configuration state */
795340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
796e01402b1SRalf Baechle 
797b618336aSKevin D. Kissell 	/*
798b618336aSKevin D. Kissell 	 * SMTC/SMVP kernels manage VPE enable independently,
799b618336aSKevin D. Kissell 	 * but uniprocessor kernels need to turn it on, even
800b618336aSKevin D. Kissell 	 * if that wasn't the pre-dvpe() state.
801b618336aSKevin D. Kissell 	 */
80207cc0c9eSRalf Baechle #ifdef CONFIG_SMP
80307cc0c9eSRalf Baechle 	evpe(vpeflags);
804b618336aSKevin D. Kissell #else
805b618336aSKevin D. Kissell 	evpe(EVPE_ENABLE);
80607cc0c9eSRalf Baechle #endif
80707cc0c9eSRalf Baechle 	emt(dmt_flag);
80807cc0c9eSRalf Baechle 	local_irq_restore(flags);
809e01402b1SRalf Baechle 
81007cc0c9eSRalf Baechle 	list_for_each_entry(n, &v->notify, list)
81107cc0c9eSRalf Baechle 		n->start(minor);
8122600990eSRalf Baechle 
813e01402b1SRalf Baechle 	return 0;
814e01402b1SRalf Baechle }
815e01402b1SRalf Baechle 
8162600990eSRalf Baechle static int find_vpe_symbols(struct vpe * v, Elf_Shdr * sechdrs,
817e01402b1SRalf Baechle 				      unsigned int symindex, const char *strtab,
818e01402b1SRalf Baechle 				      struct module *mod)
819e01402b1SRalf Baechle {
820e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
821e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
822e01402b1SRalf Baechle 
823e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
824e01402b1SRalf Baechle 		if (strcmp(strtab + sym[i].st_name, "__start") == 0) {
825e01402b1SRalf Baechle 			v->__start = sym[i].st_value;
826e01402b1SRalf Baechle 		}
827e01402b1SRalf Baechle 
828e01402b1SRalf Baechle 		if (strcmp(strtab + sym[i].st_name, "vpe_shared") == 0) {
829e01402b1SRalf Baechle 			v->shared_ptr = (void *)sym[i].st_value;
830e01402b1SRalf Baechle 		}
831e01402b1SRalf Baechle 	}
832e01402b1SRalf Baechle 
8332600990eSRalf Baechle 	if ( (v->__start == 0) || (v->shared_ptr == NULL))
8342600990eSRalf Baechle 		return -1;
8352600990eSRalf Baechle 
836e01402b1SRalf Baechle 	return 0;
837e01402b1SRalf Baechle }
838e01402b1SRalf Baechle 
839307bd284SRalf Baechle /*
8402600990eSRalf Baechle  * Allocates a VPE with some program code space(the load address), copies the
8412600990eSRalf Baechle  * contents of the program (p)buffer performing relocatations/etc, free's it
8422600990eSRalf Baechle  * when finished.
843e01402b1SRalf Baechle  */
844be6e1437SRalf Baechle static int vpe_elfload(struct vpe * v)
845e01402b1SRalf Baechle {
846e01402b1SRalf Baechle 	Elf_Ehdr *hdr;
847e01402b1SRalf Baechle 	Elf_Shdr *sechdrs;
848e01402b1SRalf Baechle 	long err = 0;
849e01402b1SRalf Baechle 	char *secstrings, *strtab = NULL;
8502600990eSRalf Baechle 	unsigned int len, i, symindex = 0, strindex = 0, relocate = 0;
851e01402b1SRalf Baechle 	struct module mod;	// so we can re-use the relocations code
852e01402b1SRalf Baechle 
853e01402b1SRalf Baechle 	memset(&mod, 0, sizeof(struct module));
8542600990eSRalf Baechle 	strcpy(mod.name, "VPE loader");
855e01402b1SRalf Baechle 
856e01402b1SRalf Baechle 	hdr = (Elf_Ehdr *) v->pbuffer;
857e01402b1SRalf Baechle 	len = v->plen;
858e01402b1SRalf Baechle 
859e01402b1SRalf Baechle 	/* Sanity checks against insmoding binaries or wrong arch,
860e01402b1SRalf Baechle 	   weird elf version */
861d303f4a1SCyrill Gorcunov 	if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0
8622600990eSRalf Baechle 	    || (hdr->e_type != ET_REL && hdr->e_type != ET_EXEC)
8632600990eSRalf Baechle 	    || !elf_check_arch(hdr)
864e01402b1SRalf Baechle 	    || hdr->e_shentsize != sizeof(*sechdrs)) {
865e01402b1SRalf Baechle 		printk(KERN_WARNING
8662600990eSRalf Baechle 		       "VPE loader: program wrong arch or weird elf version\n");
867e01402b1SRalf Baechle 
868e01402b1SRalf Baechle 		return -ENOEXEC;
869e01402b1SRalf Baechle 	}
870e01402b1SRalf Baechle 
8712600990eSRalf Baechle 	if (hdr->e_type == ET_REL)
8722600990eSRalf Baechle 		relocate = 1;
8732600990eSRalf Baechle 
874e01402b1SRalf Baechle 	if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) {
8752600990eSRalf Baechle 		printk(KERN_ERR "VPE loader: program length %u truncated\n",
8762600990eSRalf Baechle 		       len);
8772600990eSRalf Baechle 
878e01402b1SRalf Baechle 		return -ENOEXEC;
879e01402b1SRalf Baechle 	}
880e01402b1SRalf Baechle 
881e01402b1SRalf Baechle 	/* Convenience variables */
882e01402b1SRalf Baechle 	sechdrs = (void *)hdr + hdr->e_shoff;
883e01402b1SRalf Baechle 	secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
884e01402b1SRalf Baechle 	sechdrs[0].sh_addr = 0;
885e01402b1SRalf Baechle 
886e01402b1SRalf Baechle 	/* And these should exist, but gcc whinges if we don't init them */
887e01402b1SRalf Baechle 	symindex = strindex = 0;
888e01402b1SRalf Baechle 
8892600990eSRalf Baechle 	if (relocate) {
890e01402b1SRalf Baechle 		for (i = 1; i < hdr->e_shnum; i++) {
891e01402b1SRalf Baechle 			if (sechdrs[i].sh_type != SHT_NOBITS
892e01402b1SRalf Baechle 			    && len < sechdrs[i].sh_offset + sechdrs[i].sh_size) {
893e01402b1SRalf Baechle 				printk(KERN_ERR "VPE program length %u truncated\n",
894e01402b1SRalf Baechle 				       len);
895e01402b1SRalf Baechle 				return -ENOEXEC;
896e01402b1SRalf Baechle 			}
897e01402b1SRalf Baechle 
898e01402b1SRalf Baechle 			/* Mark all sections sh_addr with their address in the
899e01402b1SRalf Baechle 			   temporary image. */
900e01402b1SRalf Baechle 			sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
901e01402b1SRalf Baechle 
902e01402b1SRalf Baechle 			/* Internal symbols and strings. */
903e01402b1SRalf Baechle 			if (sechdrs[i].sh_type == SHT_SYMTAB) {
904e01402b1SRalf Baechle 				symindex = i;
905e01402b1SRalf Baechle 				strindex = sechdrs[i].sh_link;
906e01402b1SRalf Baechle 				strtab = (char *)hdr + sechdrs[strindex].sh_offset;
907e01402b1SRalf Baechle 			}
908e01402b1SRalf Baechle 		}
909e01402b1SRalf Baechle 		layout_sections(&mod, hdr, sechdrs, secstrings);
9102600990eSRalf Baechle 	}
911e01402b1SRalf Baechle 
912e01402b1SRalf Baechle 	v->load_addr = alloc_progmem(mod.core_size);
9135408c490SRalf Baechle 	if (!v->load_addr)
9145408c490SRalf Baechle 		return -ENOMEM;
915e01402b1SRalf Baechle 
9165408c490SRalf Baechle 	pr_info("VPE loader: loading to %p\n", v->load_addr);
917e01402b1SRalf Baechle 
9182600990eSRalf Baechle 	if (relocate) {
919e01402b1SRalf Baechle 		for (i = 0; i < hdr->e_shnum; i++) {
920e01402b1SRalf Baechle 			void *dest;
921e01402b1SRalf Baechle 
922e01402b1SRalf Baechle 			if (!(sechdrs[i].sh_flags & SHF_ALLOC))
923e01402b1SRalf Baechle 				continue;
924e01402b1SRalf Baechle 
925e01402b1SRalf Baechle 			dest = v->load_addr + sechdrs[i].sh_entsize;
926e01402b1SRalf Baechle 
927e01402b1SRalf Baechle 			if (sechdrs[i].sh_type != SHT_NOBITS)
928e01402b1SRalf Baechle 				memcpy(dest, (void *)sechdrs[i].sh_addr,
929e01402b1SRalf Baechle 				       sechdrs[i].sh_size);
930e01402b1SRalf Baechle 			/* Update sh_addr to point to copy in image. */
931e01402b1SRalf Baechle 			sechdrs[i].sh_addr = (unsigned long)dest;
9322600990eSRalf Baechle 
9332600990eSRalf Baechle 			printk(KERN_DEBUG " section sh_name %s sh_addr 0x%x\n",
9342600990eSRalf Baechle 			       secstrings + sechdrs[i].sh_name, sechdrs[i].sh_addr);
935e01402b1SRalf Baechle 		}
936e01402b1SRalf Baechle 
937e01402b1SRalf Baechle  		/* Fix up syms, so that st_value is a pointer to location. */
938e01402b1SRalf Baechle  		simplify_symbols(sechdrs, symindex, strtab, secstrings,
939e01402b1SRalf Baechle  				 hdr->e_shnum, &mod);
940e01402b1SRalf Baechle 
941e01402b1SRalf Baechle  		/* Now do relocations. */
942e01402b1SRalf Baechle  		for (i = 1; i < hdr->e_shnum; i++) {
943e01402b1SRalf Baechle  			const char *strtab = (char *)sechdrs[strindex].sh_addr;
944e01402b1SRalf Baechle  			unsigned int info = sechdrs[i].sh_info;
945e01402b1SRalf Baechle 
946e01402b1SRalf Baechle  			/* Not a valid relocation section? */
947e01402b1SRalf Baechle  			if (info >= hdr->e_shnum)
948e01402b1SRalf Baechle  				continue;
949e01402b1SRalf Baechle 
950e01402b1SRalf Baechle  			/* Don't bother with non-allocated sections */
951e01402b1SRalf Baechle  			if (!(sechdrs[info].sh_flags & SHF_ALLOC))
952e01402b1SRalf Baechle  				continue;
953e01402b1SRalf Baechle 
954e01402b1SRalf Baechle  			if (sechdrs[i].sh_type == SHT_REL)
9552600990eSRalf Baechle  				err = apply_relocations(sechdrs, strtab, symindex, i,
9562600990eSRalf Baechle  							&mod);
957e01402b1SRalf Baechle  			else if (sechdrs[i].sh_type == SHT_RELA)
958e01402b1SRalf Baechle  				err = apply_relocate_add(sechdrs, strtab, symindex, i,
959e01402b1SRalf Baechle  							 &mod);
9602600990eSRalf Baechle  			if (err < 0)
9612600990eSRalf Baechle  				return err;
9622600990eSRalf Baechle 
9632600990eSRalf Baechle   		}
9642600990eSRalf Baechle   	} else {
965bdf5d42cSRalf Baechle 		struct elf_phdr *phdr = (struct elf_phdr *) ((char *)hdr + hdr->e_phoff);
9662600990eSRalf Baechle 
967bdf5d42cSRalf Baechle 		for (i = 0; i < hdr->e_phnum; i++) {
968b618336aSKevin D. Kissell 			if (phdr->p_type == PT_LOAD) {
969b618336aSKevin D. Kissell 				memcpy((void *)phdr->p_paddr,
970b618336aSKevin D. Kissell 				       (char *)hdr + phdr->p_offset,
971b618336aSKevin D. Kissell 				       phdr->p_filesz);
972b618336aSKevin D. Kissell 				memset((void *)phdr->p_paddr + phdr->p_filesz,
973b618336aSKevin D. Kissell 				       0, phdr->p_memsz - phdr->p_filesz);
974b618336aSKevin D. Kissell 		    }
975bdf5d42cSRalf Baechle 		    phdr++;
976bdf5d42cSRalf Baechle 		}
977bdf5d42cSRalf Baechle 
978bdf5d42cSRalf Baechle 		for (i = 0; i < hdr->e_shnum; i++) {
9792600990eSRalf Baechle  			/* Internal symbols and strings. */
9802600990eSRalf Baechle  			if (sechdrs[i].sh_type == SHT_SYMTAB) {
9812600990eSRalf Baechle  				symindex = i;
9822600990eSRalf Baechle  				strindex = sechdrs[i].sh_link;
9832600990eSRalf Baechle  				strtab = (char *)hdr + sechdrs[strindex].sh_offset;
9842600990eSRalf Baechle 
9852600990eSRalf Baechle  				/* mark the symtab's address for when we try to find the
9862600990eSRalf Baechle  				   magic symbols */
9872600990eSRalf Baechle  				sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
9882600990eSRalf Baechle  			}
989e01402b1SRalf Baechle 		}
990e01402b1SRalf Baechle 	}
991e01402b1SRalf Baechle 
992e01402b1SRalf Baechle 	/* make sure it's physically written out */
993e01402b1SRalf Baechle 	flush_icache_range((unsigned long)v->load_addr,
994e01402b1SRalf Baechle 			   (unsigned long)v->load_addr + v->len);
995e01402b1SRalf Baechle 
996e01402b1SRalf Baechle 	if ((find_vpe_symbols(v, sechdrs, symindex, strtab, &mod)) < 0) {
9972600990eSRalf Baechle 		if (v->__start == 0) {
9982600990eSRalf Baechle 			printk(KERN_WARNING "VPE loader: program does not contain "
9992600990eSRalf Baechle 			       "a __start symbol\n");
10002600990eSRalf Baechle 			return -ENOEXEC;
10012600990eSRalf Baechle 		}
1002e01402b1SRalf Baechle 
10032600990eSRalf Baechle 		if (v->shared_ptr == NULL)
10042600990eSRalf Baechle 			printk(KERN_WARNING "VPE loader: "
10052600990eSRalf Baechle 			       "program does not contain vpe_shared symbol.\n"
10062600990eSRalf Baechle 			       " Unable to use AMVP (AP/SP) facilities.\n");
1007e01402b1SRalf Baechle 	}
1008e01402b1SRalf Baechle 
1009e01402b1SRalf Baechle 	printk(" elf loaded\n");
10102600990eSRalf Baechle 	return 0;
1011e01402b1SRalf Baechle }
1012e01402b1SRalf Baechle 
10132600990eSRalf Baechle static void cleanup_tc(struct tc *tc)
1014e01402b1SRalf Baechle {
101507cc0c9eSRalf Baechle 	unsigned long flags;
101607cc0c9eSRalf Baechle 	unsigned int mtflags, vpflags;
10172600990eSRalf Baechle 	int tmp;
1018e01402b1SRalf Baechle 
101907cc0c9eSRalf Baechle 	local_irq_save(flags);
102007cc0c9eSRalf Baechle 	mtflags = dmt();
102107cc0c9eSRalf Baechle 	vpflags = dvpe();
10222600990eSRalf Baechle 	/* Put MVPE's into 'configuration state' */
10232600990eSRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1024e01402b1SRalf Baechle 
10252600990eSRalf Baechle 	settc(tc->index);
1026e01402b1SRalf Baechle 	tmp = read_tc_c0_tcstatus();
1027e01402b1SRalf Baechle 
1028e01402b1SRalf Baechle 	/* mark not allocated and not dynamically allocatable */
1029e01402b1SRalf Baechle 	tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1030e01402b1SRalf Baechle 	tmp |= TCSTATUS_IXMT;	/* interrupt exempt */
1031e01402b1SRalf Baechle 	write_tc_c0_tcstatus(tmp);
1032e01402b1SRalf Baechle 
1033e01402b1SRalf Baechle 	write_tc_c0_tchalt(TCHALT_H);
10347c3a622dSNigel Stephens 	mips_ihb();
1035e01402b1SRalf Baechle 
10362600990eSRalf Baechle 	/* bind it to anything other than VPE1 */
103707cc0c9eSRalf Baechle //	write_tc_c0_tcbind(read_tc_c0_tcbind() & ~TCBIND_CURVPE); // | TCBIND_CURVPE
10382600990eSRalf Baechle 
10392600990eSRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
104007cc0c9eSRalf Baechle 	evpe(vpflags);
104107cc0c9eSRalf Baechle 	emt(mtflags);
104207cc0c9eSRalf Baechle 	local_irq_restore(flags);
10432600990eSRalf Baechle }
10442600990eSRalf Baechle 
10452600990eSRalf Baechle static int getcwd(char *buff, int size)
10462600990eSRalf Baechle {
10472600990eSRalf Baechle 	mm_segment_t old_fs;
10482600990eSRalf Baechle 	int ret;
10492600990eSRalf Baechle 
10502600990eSRalf Baechle 	old_fs = get_fs();
10512600990eSRalf Baechle 	set_fs(KERNEL_DS);
10522600990eSRalf Baechle 
10532600990eSRalf Baechle 	ret = sys_getcwd(buff, size);
10542600990eSRalf Baechle 
10552600990eSRalf Baechle 	set_fs(old_fs);
10562600990eSRalf Baechle 
10572600990eSRalf Baechle 	return ret;
10582600990eSRalf Baechle }
10592600990eSRalf Baechle 
10602600990eSRalf Baechle /* checks VPE is unused and gets ready to load program  */
10612600990eSRalf Baechle static int vpe_open(struct inode *inode, struct file *filp)
10622600990eSRalf Baechle {
1063c4c4018bSRalf Baechle 	enum vpe_state state;
10642600990eSRalf Baechle 	struct vpe_notifications *not;
106507cc0c9eSRalf Baechle 	struct vpe *v;
10661bbfc20dSRalf Baechle 	int ret;
10672600990eSRalf Baechle 
106807cc0c9eSRalf Baechle 	if (minor != iminor(inode)) {
106907cc0c9eSRalf Baechle 		/* assume only 1 device at the moment. */
10701bbfc20dSRalf Baechle 		pr_warning("VPE loader: only vpe1 is supported\n");
10711bbfc20dSRalf Baechle 
10721bbfc20dSRalf Baechle 		return -ENODEV;
10732600990eSRalf Baechle 	}
10742600990eSRalf Baechle 
107507cc0c9eSRalf Baechle 	if ((v = get_vpe(tclimit)) == NULL) {
10761bbfc20dSRalf Baechle 		pr_warning("VPE loader: unable to get vpe\n");
10771bbfc20dSRalf Baechle 
10781bbfc20dSRalf Baechle 		return -ENODEV;
10792600990eSRalf Baechle 	}
10802600990eSRalf Baechle 
1081c4c4018bSRalf Baechle 	state = xchg(&v->state, VPE_STATE_INUSE);
1082c4c4018bSRalf Baechle 	if (state != VPE_STATE_UNUSED) {
10832600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: tc in use dumping regs\n");
10842600990eSRalf Baechle 
10852600990eSRalf Baechle 		list_for_each_entry(not, &v->notify, list) {
108607cc0c9eSRalf Baechle 			not->stop(tclimit);
10872600990eSRalf Baechle 		}
10882600990eSRalf Baechle 
10892600990eSRalf Baechle 		release_progmem(v->load_addr);
109007cc0c9eSRalf Baechle 		cleanup_tc(get_tc(tclimit));
1091e01402b1SRalf Baechle 	}
1092e01402b1SRalf Baechle 
1093e01402b1SRalf Baechle 	/* this of-course trashes what was there before... */
1094e01402b1SRalf Baechle 	v->pbuffer = vmalloc(P_SIZE);
1095863abad4SJesper Juhl 	if (!v->pbuffer) {
1096863abad4SJesper Juhl 		pr_warning("VPE loader: unable to allocate memory\n");
1097863abad4SJesper Juhl 		return -ENOMEM;
1098863abad4SJesper Juhl 	}
1099e01402b1SRalf Baechle 	v->plen = P_SIZE;
1100e01402b1SRalf Baechle 	v->load_addr = NULL;
1101e01402b1SRalf Baechle 	v->len = 0;
1102e01402b1SRalf Baechle 
1103d76b0d9bSDavid Howells 	v->uid = filp->f_cred->fsuid;
1104d76b0d9bSDavid Howells 	v->gid = filp->f_cred->fsgid;
11052600990eSRalf Baechle 
11062600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
11072600990eSRalf Baechle 	/* get kspd to tell us when a syscall_exit happens */
11082600990eSRalf Baechle 	if (!kspd_events_reqd) {
11092600990eSRalf Baechle 		kspd_notify(&kspd_events);
11102600990eSRalf Baechle 		kspd_events_reqd++;
11112600990eSRalf Baechle 	}
11122600990eSRalf Baechle #endif
11132600990eSRalf Baechle 
11142600990eSRalf Baechle 	v->cwd[0] = 0;
11152600990eSRalf Baechle 	ret = getcwd(v->cwd, VPE_PATH_MAX);
11162600990eSRalf Baechle 	if (ret < 0)
11172600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: open, getcwd returned %d\n", ret);
11182600990eSRalf Baechle 
11192600990eSRalf Baechle 	v->shared_ptr = NULL;
11202600990eSRalf Baechle 	v->__start = 0;
112107cc0c9eSRalf Baechle 
1122e01402b1SRalf Baechle 	return 0;
1123e01402b1SRalf Baechle }
1124e01402b1SRalf Baechle 
1125e01402b1SRalf Baechle static int vpe_release(struct inode *inode, struct file *filp)
1126e01402b1SRalf Baechle {
1127307bd284SRalf Baechle 	struct vpe *v;
1128e01402b1SRalf Baechle 	Elf_Ehdr *hdr;
112907cc0c9eSRalf Baechle 	int ret = 0;
1130e01402b1SRalf Baechle 
113107cc0c9eSRalf Baechle 	v = get_vpe(tclimit);
113207cc0c9eSRalf Baechle 	if (v == NULL)
1133e01402b1SRalf Baechle 		return -ENODEV;
1134e01402b1SRalf Baechle 
1135e01402b1SRalf Baechle 	hdr = (Elf_Ehdr *) v->pbuffer;
1136d303f4a1SCyrill Gorcunov 	if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) == 0) {
113707cc0c9eSRalf Baechle 		if (vpe_elfload(v) >= 0) {
1138e01402b1SRalf Baechle 			vpe_run(v);
113907cc0c9eSRalf Baechle 		} else {
11402600990eSRalf Baechle  			printk(KERN_WARNING "VPE loader: ELF load failed.\n");
1141e01402b1SRalf Baechle 			ret = -ENOEXEC;
1142e01402b1SRalf Baechle 		}
1143e01402b1SRalf Baechle 	} else {
11442600990eSRalf Baechle  		printk(KERN_WARNING "VPE loader: only elf files are supported\n");
1145e01402b1SRalf Baechle 		ret = -ENOEXEC;
1146e01402b1SRalf Baechle 	}
1147e01402b1SRalf Baechle 
11482600990eSRalf Baechle 	/* It's good to be able to run the SP and if it chokes have a look at
11492600990eSRalf Baechle 	   the /dev/rt?. But if we reset the pointer to the shared struct we
11508ebcfc8bSNick Andrew 	   lose what has happened. So perhaps if garbage is sent to the vpe
11512600990eSRalf Baechle 	   device, use it as a trigger for the reset. Hopefully a nice
11522600990eSRalf Baechle 	   executable will be along shortly. */
11532600990eSRalf Baechle 	if (ret < 0)
11542600990eSRalf Baechle 		v->shared_ptr = NULL;
11552600990eSRalf Baechle 
1156e01402b1SRalf Baechle 	vfree(v->pbuffer);
1157e01402b1SRalf Baechle 	v->plen = 0;
1158863abad4SJesper Juhl 
1159e01402b1SRalf Baechle 	return ret;
1160e01402b1SRalf Baechle }
1161e01402b1SRalf Baechle 
1162e01402b1SRalf Baechle static ssize_t vpe_write(struct file *file, const char __user * buffer,
1163e01402b1SRalf Baechle 			 size_t count, loff_t * ppos)
1164e01402b1SRalf Baechle {
1165e01402b1SRalf Baechle 	size_t ret = count;
1166307bd284SRalf Baechle 	struct vpe *v;
1167e01402b1SRalf Baechle 
116807cc0c9eSRalf Baechle 	if (iminor(file->f_path.dentry->d_inode) != minor)
116907cc0c9eSRalf Baechle 		return -ENODEV;
117007cc0c9eSRalf Baechle 
117107cc0c9eSRalf Baechle 	v = get_vpe(tclimit);
117207cc0c9eSRalf Baechle 	if (v == NULL)
1173e01402b1SRalf Baechle 		return -ENODEV;
1174e01402b1SRalf Baechle 
1175e01402b1SRalf Baechle 	if ((count + v->len) > v->plen) {
1176e01402b1SRalf Baechle 		printk(KERN_WARNING
11772600990eSRalf Baechle 		       "VPE loader: elf size too big. Perhaps strip uneeded symbols\n");
1178e01402b1SRalf Baechle 		return -ENOMEM;
1179e01402b1SRalf Baechle 	}
1180e01402b1SRalf Baechle 
1181e01402b1SRalf Baechle 	count -= copy_from_user(v->pbuffer + v->len, buffer, count);
11822600990eSRalf Baechle 	if (!count)
1183e01402b1SRalf Baechle 		return -EFAULT;
1184e01402b1SRalf Baechle 
1185e01402b1SRalf Baechle 	v->len += count;
1186e01402b1SRalf Baechle 	return ret;
1187e01402b1SRalf Baechle }
1188e01402b1SRalf Baechle 
11895dfe4c96SArjan van de Ven static const struct file_operations vpe_fops = {
1190e01402b1SRalf Baechle 	.owner = THIS_MODULE,
1191e01402b1SRalf Baechle 	.open = vpe_open,
1192e01402b1SRalf Baechle 	.release = vpe_release,
11936038f373SArnd Bergmann 	.write = vpe_write,
11946038f373SArnd Bergmann 	.llseek = noop_llseek,
1195e01402b1SRalf Baechle };
1196e01402b1SRalf Baechle 
1197e01402b1SRalf Baechle /* module wrapper entry points */
1198e01402b1SRalf Baechle /* give me a vpe */
1199e01402b1SRalf Baechle vpe_handle vpe_alloc(void)
1200e01402b1SRalf Baechle {
1201e01402b1SRalf Baechle 	int i;
1202e01402b1SRalf Baechle 	struct vpe *v;
1203e01402b1SRalf Baechle 
1204e01402b1SRalf Baechle 	/* find a vpe */
1205e01402b1SRalf Baechle 	for (i = 1; i < MAX_VPES; i++) {
1206e01402b1SRalf Baechle 		if ((v = get_vpe(i)) != NULL) {
1207e01402b1SRalf Baechle 			v->state = VPE_STATE_INUSE;
1208e01402b1SRalf Baechle 			return v;
1209e01402b1SRalf Baechle 		}
1210e01402b1SRalf Baechle 	}
1211e01402b1SRalf Baechle 	return NULL;
1212e01402b1SRalf Baechle }
1213e01402b1SRalf Baechle 
1214e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_alloc);
1215e01402b1SRalf Baechle 
1216e01402b1SRalf Baechle /* start running from here */
1217e01402b1SRalf Baechle int vpe_start(vpe_handle vpe, unsigned long start)
1218e01402b1SRalf Baechle {
1219e01402b1SRalf Baechle 	struct vpe *v = vpe;
1220e01402b1SRalf Baechle 
1221e01402b1SRalf Baechle 	v->__start = start;
1222e01402b1SRalf Baechle 	return vpe_run(v);
1223e01402b1SRalf Baechle }
1224e01402b1SRalf Baechle 
1225e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_start);
1226e01402b1SRalf Baechle 
1227e01402b1SRalf Baechle /* halt it for now */
1228e01402b1SRalf Baechle int vpe_stop(vpe_handle vpe)
1229e01402b1SRalf Baechle {
1230e01402b1SRalf Baechle 	struct vpe *v = vpe;
1231e01402b1SRalf Baechle 	struct tc *t;
1232e01402b1SRalf Baechle 	unsigned int evpe_flags;
1233e01402b1SRalf Baechle 
1234e01402b1SRalf Baechle 	evpe_flags = dvpe();
1235e01402b1SRalf Baechle 
1236e01402b1SRalf Baechle 	if ((t = list_entry(v->tc.next, struct tc, tc)) != NULL) {
1237e01402b1SRalf Baechle 
1238e01402b1SRalf Baechle 		settc(t->index);
1239e01402b1SRalf Baechle 		write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
1240e01402b1SRalf Baechle 	}
1241e01402b1SRalf Baechle 
1242e01402b1SRalf Baechle 	evpe(evpe_flags);
1243e01402b1SRalf Baechle 
1244e01402b1SRalf Baechle 	return 0;
1245e01402b1SRalf Baechle }
1246e01402b1SRalf Baechle 
1247e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_stop);
1248e01402b1SRalf Baechle 
1249e01402b1SRalf Baechle /* I've done with it thank you */
1250e01402b1SRalf Baechle int vpe_free(vpe_handle vpe)
1251e01402b1SRalf Baechle {
1252e01402b1SRalf Baechle 	struct vpe *v = vpe;
1253e01402b1SRalf Baechle 	struct tc *t;
1254e01402b1SRalf Baechle 	unsigned int evpe_flags;
1255e01402b1SRalf Baechle 
1256e01402b1SRalf Baechle 	if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
1257e01402b1SRalf Baechle 		return -ENOEXEC;
1258e01402b1SRalf Baechle 	}
1259e01402b1SRalf Baechle 
1260e01402b1SRalf Baechle 	evpe_flags = dvpe();
1261e01402b1SRalf Baechle 
1262e01402b1SRalf Baechle 	/* Put MVPE's into 'configuration state' */
1263340ee4b9SRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1264e01402b1SRalf Baechle 
1265e01402b1SRalf Baechle 	settc(t->index);
1266e01402b1SRalf Baechle 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
1267e01402b1SRalf Baechle 
12687c3a622dSNigel Stephens 	/* halt the TC */
1269e01402b1SRalf Baechle 	write_tc_c0_tchalt(TCHALT_H);
12707c3a622dSNigel Stephens 	mips_ihb();
12717c3a622dSNigel Stephens 
12727c3a622dSNigel Stephens 	/* mark the TC unallocated */
12737c3a622dSNigel Stephens 	write_tc_c0_tcstatus(read_tc_c0_tcstatus() & ~TCSTATUS_A);
1274e01402b1SRalf Baechle 
1275e01402b1SRalf Baechle 	v->state = VPE_STATE_UNUSED;
1276e01402b1SRalf Baechle 
1277340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
1278e01402b1SRalf Baechle 	evpe(evpe_flags);
1279e01402b1SRalf Baechle 
1280e01402b1SRalf Baechle 	return 0;
1281e01402b1SRalf Baechle }
1282e01402b1SRalf Baechle 
1283e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_free);
1284e01402b1SRalf Baechle 
1285e01402b1SRalf Baechle void *vpe_get_shared(int index)
1286e01402b1SRalf Baechle {
1287e01402b1SRalf Baechle 	struct vpe *v;
1288e01402b1SRalf Baechle 
12892600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
1290e01402b1SRalf Baechle 		return NULL;
1291e01402b1SRalf Baechle 
1292e01402b1SRalf Baechle 	return v->shared_ptr;
1293e01402b1SRalf Baechle }
1294e01402b1SRalf Baechle 
1295e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_get_shared);
1296e01402b1SRalf Baechle 
12972600990eSRalf Baechle int vpe_getuid(int index)
12982600990eSRalf Baechle {
12992600990eSRalf Baechle 	struct vpe *v;
13002600990eSRalf Baechle 
13012600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13022600990eSRalf Baechle 		return -1;
13032600990eSRalf Baechle 
13042600990eSRalf Baechle 	return v->uid;
13052600990eSRalf Baechle }
13062600990eSRalf Baechle 
13072600990eSRalf Baechle EXPORT_SYMBOL(vpe_getuid);
13082600990eSRalf Baechle 
13092600990eSRalf Baechle int vpe_getgid(int index)
13102600990eSRalf Baechle {
13112600990eSRalf Baechle 	struct vpe *v;
13122600990eSRalf Baechle 
13132600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13142600990eSRalf Baechle 		return -1;
13152600990eSRalf Baechle 
13162600990eSRalf Baechle 	return v->gid;
13172600990eSRalf Baechle }
13182600990eSRalf Baechle 
13192600990eSRalf Baechle EXPORT_SYMBOL(vpe_getgid);
13202600990eSRalf Baechle 
13212600990eSRalf Baechle int vpe_notify(int index, struct vpe_notifications *notify)
13222600990eSRalf Baechle {
13232600990eSRalf Baechle 	struct vpe *v;
13242600990eSRalf Baechle 
13252600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13262600990eSRalf Baechle 		return -1;
13272600990eSRalf Baechle 
13282600990eSRalf Baechle 	list_add(&notify->list, &v->notify);
13292600990eSRalf Baechle 	return 0;
13302600990eSRalf Baechle }
13312600990eSRalf Baechle 
13322600990eSRalf Baechle EXPORT_SYMBOL(vpe_notify);
13332600990eSRalf Baechle 
13342600990eSRalf Baechle char *vpe_getcwd(int index)
13352600990eSRalf Baechle {
13362600990eSRalf Baechle 	struct vpe *v;
13372600990eSRalf Baechle 
13382600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13392600990eSRalf Baechle 		return NULL;
13402600990eSRalf Baechle 
13412600990eSRalf Baechle 	return v->cwd;
13422600990eSRalf Baechle }
13432600990eSRalf Baechle 
13442600990eSRalf Baechle EXPORT_SYMBOL(vpe_getcwd);
13452600990eSRalf Baechle 
13462600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
13472600990eSRalf Baechle static void kspd_sp_exit( int sp_id)
13482600990eSRalf Baechle {
13492600990eSRalf Baechle 	cleanup_tc(get_tc(sp_id));
13502600990eSRalf Baechle }
13512600990eSRalf Baechle #endif
13522600990eSRalf Baechle 
1353736fad17SKay Sievers static ssize_t store_kill(struct device *dev, struct device_attribute *attr,
1354736fad17SKay Sievers 			  const char *buf, size_t len)
13550f5d0df3SRalf Baechle {
13560f5d0df3SRalf Baechle 	struct vpe *vpe = get_vpe(tclimit);
13570f5d0df3SRalf Baechle 	struct vpe_notifications *not;
13580f5d0df3SRalf Baechle 
13590f5d0df3SRalf Baechle 	list_for_each_entry(not, &vpe->notify, list) {
13600f5d0df3SRalf Baechle 		not->stop(tclimit);
13610f5d0df3SRalf Baechle 	}
13620f5d0df3SRalf Baechle 
13630f5d0df3SRalf Baechle 	release_progmem(vpe->load_addr);
13640f5d0df3SRalf Baechle 	cleanup_tc(get_tc(tclimit));
13650f5d0df3SRalf Baechle 	vpe_stop(vpe);
13660f5d0df3SRalf Baechle 	vpe_free(vpe);
13670f5d0df3SRalf Baechle 
13680f5d0df3SRalf Baechle 	return len;
13690f5d0df3SRalf Baechle }
13700f5d0df3SRalf Baechle 
1371736fad17SKay Sievers static ssize_t show_ntcs(struct device *cd, struct device_attribute *attr,
1372736fad17SKay Sievers 			 char *buf)
137341790e04SRalf Baechle {
137441790e04SRalf Baechle 	struct vpe *vpe = get_vpe(tclimit);
137541790e04SRalf Baechle 
137641790e04SRalf Baechle 	return sprintf(buf, "%d\n", vpe->ntcs);
137741790e04SRalf Baechle }
137841790e04SRalf Baechle 
1379736fad17SKay Sievers static ssize_t store_ntcs(struct device *dev, struct device_attribute *attr,
1380736fad17SKay Sievers 			  const char *buf, size_t len)
138141790e04SRalf Baechle {
138241790e04SRalf Baechle 	struct vpe *vpe = get_vpe(tclimit);
138341790e04SRalf Baechle 	unsigned long new;
138441790e04SRalf Baechle 	char *endp;
138541790e04SRalf Baechle 
138641790e04SRalf Baechle 	new = simple_strtoul(buf, &endp, 0);
138741790e04SRalf Baechle 	if (endp == buf)
138841790e04SRalf Baechle 		goto out_einval;
138941790e04SRalf Baechle 
139041790e04SRalf Baechle 	if (new == 0 || new > (hw_tcs - tclimit))
139141790e04SRalf Baechle 		goto out_einval;
139241790e04SRalf Baechle 
139341790e04SRalf Baechle 	vpe->ntcs = new;
139441790e04SRalf Baechle 
139541790e04SRalf Baechle 	return len;
139641790e04SRalf Baechle 
139741790e04SRalf Baechle out_einval:
139852a7a27cSJoe Perches 	return -EINVAL;
139941790e04SRalf Baechle }
140041790e04SRalf Baechle 
1401736fad17SKay Sievers static struct device_attribute vpe_class_attributes[] = {
14020f5d0df3SRalf Baechle 	__ATTR(kill, S_IWUSR, NULL, store_kill),
140341790e04SRalf Baechle 	__ATTR(ntcs, S_IRUGO | S_IWUSR, show_ntcs, store_ntcs),
140441790e04SRalf Baechle 	{}
140541790e04SRalf Baechle };
140641790e04SRalf Baechle 
1407736fad17SKay Sievers static void vpe_device_release(struct device *cd)
140841790e04SRalf Baechle {
140941790e04SRalf Baechle 	kfree(cd);
141041790e04SRalf Baechle }
141141790e04SRalf Baechle 
141241790e04SRalf Baechle struct class vpe_class = {
141341790e04SRalf Baechle 	.name = "vpe",
141441790e04SRalf Baechle 	.owner = THIS_MODULE,
1415736fad17SKay Sievers 	.dev_release = vpe_device_release,
1416736fad17SKay Sievers 	.dev_attrs = vpe_class_attributes,
141741790e04SRalf Baechle };
141841790e04SRalf Baechle 
1419736fad17SKay Sievers struct device vpe_device;
142027a3bbafSRalf Baechle 
1421e01402b1SRalf Baechle static int __init vpe_module_init(void)
1422e01402b1SRalf Baechle {
142307cc0c9eSRalf Baechle 	unsigned int mtflags, vpflags;
142407cc0c9eSRalf Baechle 	unsigned long flags, val;
1425e01402b1SRalf Baechle 	struct vpe *v = NULL;
1426e01402b1SRalf Baechle 	struct tc *t;
142741790e04SRalf Baechle 	int tc, err;
1428e01402b1SRalf Baechle 
1429e01402b1SRalf Baechle 	if (!cpu_has_mipsmt) {
1430e01402b1SRalf Baechle 		printk("VPE loader: not a MIPS MT capable processor\n");
1431e01402b1SRalf Baechle 		return -ENODEV;
1432e01402b1SRalf Baechle 	}
1433e01402b1SRalf Baechle 
143407cc0c9eSRalf Baechle 	if (vpelimit == 0) {
143507cc0c9eSRalf Baechle 		printk(KERN_WARNING "No VPEs reserved for AP/SP, not "
143607cc0c9eSRalf Baechle 		       "initializing VPE loader.\nPass maxvpes=<n> argument as "
143707cc0c9eSRalf Baechle 		       "kernel argument\n");
143807cc0c9eSRalf Baechle 
143907cc0c9eSRalf Baechle 		return -ENODEV;
144007cc0c9eSRalf Baechle 	}
144107cc0c9eSRalf Baechle 
144207cc0c9eSRalf Baechle 	if (tclimit == 0) {
144307cc0c9eSRalf Baechle 		printk(KERN_WARNING "No TCs reserved for AP/SP, not "
144407cc0c9eSRalf Baechle 		       "initializing VPE loader.\nPass maxtcs=<n> argument as "
144507cc0c9eSRalf Baechle 		       "kernel argument\n");
144607cc0c9eSRalf Baechle 
144707cc0c9eSRalf Baechle 		return -ENODEV;
144807cc0c9eSRalf Baechle 	}
144907cc0c9eSRalf Baechle 
1450682e852eSAlexey Dobriyan 	major = register_chrdev(0, module_name, &vpe_fops);
1451682e852eSAlexey Dobriyan 	if (major < 0) {
1452e01402b1SRalf Baechle 		printk("VPE loader: unable to register character device\n");
1453307bd284SRalf Baechle 		return major;
1454e01402b1SRalf Baechle 	}
1455e01402b1SRalf Baechle 
145641790e04SRalf Baechle 	err = class_register(&vpe_class);
145741790e04SRalf Baechle 	if (err) {
145841790e04SRalf Baechle 		printk(KERN_ERR "vpe_class registration failed\n");
145927a3bbafSRalf Baechle 		goto out_chrdev;
146027a3bbafSRalf Baechle 	}
146141790e04SRalf Baechle 
1462736fad17SKay Sievers 	device_initialize(&vpe_device);
146341790e04SRalf Baechle 	vpe_device.class	= &vpe_class,
146441790e04SRalf Baechle 	vpe_device.parent	= NULL,
14651bb5beb4SKay Sievers 	dev_set_name(&vpe_device, "vpe1");
146641790e04SRalf Baechle 	vpe_device.devt = MKDEV(major, minor);
1467736fad17SKay Sievers 	err = device_add(&vpe_device);
146841790e04SRalf Baechle 	if (err) {
146941790e04SRalf Baechle 		printk(KERN_ERR "Adding vpe_device failed\n");
147041790e04SRalf Baechle 		goto out_class;
147141790e04SRalf Baechle 	}
147227a3bbafSRalf Baechle 
147307cc0c9eSRalf Baechle 	local_irq_save(flags);
147407cc0c9eSRalf Baechle 	mtflags = dmt();
147507cc0c9eSRalf Baechle 	vpflags = dvpe();
1476e01402b1SRalf Baechle 
1477e01402b1SRalf Baechle 	/* Put MVPE's into 'configuration state' */
1478340ee4b9SRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1479e01402b1SRalf Baechle 
1480e01402b1SRalf Baechle 	/* dump_mtregs(); */
1481e01402b1SRalf Baechle 
1482e01402b1SRalf Baechle 	val = read_c0_mvpconf0();
148307cc0c9eSRalf Baechle 	hw_tcs = (val & MVPCONF0_PTC) + 1;
148407cc0c9eSRalf Baechle 	hw_vpes = ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1;
148507cc0c9eSRalf Baechle 
148607cc0c9eSRalf Baechle 	for (tc = tclimit; tc < hw_tcs; tc++) {
148707cc0c9eSRalf Baechle 		/*
148807cc0c9eSRalf Baechle 		 * Must re-enable multithreading temporarily or in case we
148907cc0c9eSRalf Baechle 		 * reschedule send IPIs or similar we might hang.
149007cc0c9eSRalf Baechle 		 */
149107cc0c9eSRalf Baechle 		clear_c0_mvpcontrol(MVPCONTROL_VPC);
149207cc0c9eSRalf Baechle 		evpe(vpflags);
149307cc0c9eSRalf Baechle 		emt(mtflags);
149407cc0c9eSRalf Baechle 		local_irq_restore(flags);
149507cc0c9eSRalf Baechle 		t = alloc_tc(tc);
149607cc0c9eSRalf Baechle 		if (!t) {
149707cc0c9eSRalf Baechle 			err = -ENOMEM;
149807cc0c9eSRalf Baechle 			goto out;
149907cc0c9eSRalf Baechle 		}
150007cc0c9eSRalf Baechle 
150107cc0c9eSRalf Baechle 		local_irq_save(flags);
150207cc0c9eSRalf Baechle 		mtflags = dmt();
150307cc0c9eSRalf Baechle 		vpflags = dvpe();
150407cc0c9eSRalf Baechle 		set_c0_mvpcontrol(MVPCONTROL_VPC);
1505e01402b1SRalf Baechle 
1506e01402b1SRalf Baechle 		/* VPE's */
150707cc0c9eSRalf Baechle 		if (tc < hw_tcs) {
150807cc0c9eSRalf Baechle 			settc(tc);
1509e01402b1SRalf Baechle 
151007cc0c9eSRalf Baechle 			if ((v = alloc_vpe(tc)) == NULL) {
1511e01402b1SRalf Baechle 				printk(KERN_WARNING "VPE: unable to allocate VPE\n");
151207cc0c9eSRalf Baechle 
151307cc0c9eSRalf Baechle 				goto out_reenable;
1514e01402b1SRalf Baechle 			}
1515e01402b1SRalf Baechle 
151641790e04SRalf Baechle 			v->ntcs = hw_tcs - tclimit;
151741790e04SRalf Baechle 
15182600990eSRalf Baechle 			/* add the tc to the list of this vpe's tc's. */
15192600990eSRalf Baechle 			list_add(&t->tc, &v->tc);
1520e01402b1SRalf Baechle 
1521e01402b1SRalf Baechle 			/* deactivate all but vpe0 */
152207cc0c9eSRalf Baechle 			if (tc >= tclimit) {
1523e01402b1SRalf Baechle 				unsigned long tmp = read_vpe_c0_vpeconf0();
1524e01402b1SRalf Baechle 
1525e01402b1SRalf Baechle 				tmp &= ~VPECONF0_VPA;
1526e01402b1SRalf Baechle 
1527e01402b1SRalf Baechle 				/* master VPE */
1528e01402b1SRalf Baechle 				tmp |= VPECONF0_MVP;
1529e01402b1SRalf Baechle 				write_vpe_c0_vpeconf0(tmp);
1530e01402b1SRalf Baechle 			}
1531e01402b1SRalf Baechle 
1532e01402b1SRalf Baechle 			/* disable multi-threading with TC's */
1533e01402b1SRalf Baechle 			write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE);
1534e01402b1SRalf Baechle 
153507cc0c9eSRalf Baechle 			if (tc >= vpelimit) {
15362600990eSRalf Baechle 				/*
15372600990eSRalf Baechle 				 * Set config to be the same as vpe0,
15382600990eSRalf Baechle 				 * particularly kseg0 coherency alg
15392600990eSRalf Baechle 				 */
1540e01402b1SRalf Baechle 				write_vpe_c0_config(read_c0_config());
1541e01402b1SRalf Baechle 			}
1542e01402b1SRalf Baechle 		}
1543e01402b1SRalf Baechle 
1544e01402b1SRalf Baechle 		/* TC's */
1545e01402b1SRalf Baechle 		t->pvpe = v;	/* set the parent vpe */
1546e01402b1SRalf Baechle 
154707cc0c9eSRalf Baechle 		if (tc >= tclimit) {
1548e01402b1SRalf Baechle 			unsigned long tmp;
1549e01402b1SRalf Baechle 
155007cc0c9eSRalf Baechle 			settc(tc);
1551e01402b1SRalf Baechle 
15522600990eSRalf Baechle 			/* Any TC that is bound to VPE0 gets left as is - in case
15532600990eSRalf Baechle 			   we are running SMTC on VPE0. A TC that is bound to any
15542600990eSRalf Baechle 			   other VPE gets bound to VPE0, ideally I'd like to make
15552600990eSRalf Baechle 			   it homeless but it doesn't appear to let me bind a TC
15562600990eSRalf Baechle 			   to a non-existent VPE. Which is perfectly reasonable.
15572600990eSRalf Baechle 
15582600990eSRalf Baechle 			   The (un)bound state is visible to an EJTAG probe so may
15592600990eSRalf Baechle 			   notify GDB...
15602600990eSRalf Baechle 			*/
15612600990eSRalf Baechle 
15622600990eSRalf Baechle 			if (((tmp = read_tc_c0_tcbind()) & TCBIND_CURVPE)) {
15632600990eSRalf Baechle 				/* tc is bound >vpe0 */
15642600990eSRalf Baechle 				write_tc_c0_tcbind(tmp & ~TCBIND_CURVPE);
15652600990eSRalf Baechle 
15662600990eSRalf Baechle 				t->pvpe = get_vpe(0);	/* set the parent vpe */
15672600990eSRalf Baechle 			}
1568e01402b1SRalf Baechle 
15697c3a622dSNigel Stephens 			/* halt the TC */
15707c3a622dSNigel Stephens 			write_tc_c0_tchalt(TCHALT_H);
15717c3a622dSNigel Stephens 			mips_ihb();
15727c3a622dSNigel Stephens 
1573e01402b1SRalf Baechle 			tmp = read_tc_c0_tcstatus();
1574e01402b1SRalf Baechle 
15752600990eSRalf Baechle 			/* mark not activated and not dynamically allocatable */
1576e01402b1SRalf Baechle 			tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1577e01402b1SRalf Baechle 			tmp |= TCSTATUS_IXMT;	/* interrupt exempt */
1578e01402b1SRalf Baechle 			write_tc_c0_tcstatus(tmp);
1579e01402b1SRalf Baechle 		}
1580e01402b1SRalf Baechle 	}
1581e01402b1SRalf Baechle 
158207cc0c9eSRalf Baechle out_reenable:
1583e01402b1SRalf Baechle 	/* release config state */
1584340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
1585e01402b1SRalf Baechle 
158607cc0c9eSRalf Baechle 	evpe(vpflags);
158707cc0c9eSRalf Baechle 	emt(mtflags);
158807cc0c9eSRalf Baechle 	local_irq_restore(flags);
158907cc0c9eSRalf Baechle 
15902600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
15912600990eSRalf Baechle 	kspd_events.kspd_sp_exit = kspd_sp_exit;
15922600990eSRalf Baechle #endif
1593e01402b1SRalf Baechle 	return 0;
159427a3bbafSRalf Baechle 
159541790e04SRalf Baechle out_class:
159641790e04SRalf Baechle 	class_unregister(&vpe_class);
159727a3bbafSRalf Baechle out_chrdev:
159827a3bbafSRalf Baechle 	unregister_chrdev(major, module_name);
159927a3bbafSRalf Baechle 
160007cc0c9eSRalf Baechle out:
160127a3bbafSRalf Baechle 	return err;
1602e01402b1SRalf Baechle }
1603e01402b1SRalf Baechle 
1604e01402b1SRalf Baechle static void __exit vpe_module_exit(void)
1605e01402b1SRalf Baechle {
1606e01402b1SRalf Baechle 	struct vpe *v, *n;
1607e01402b1SRalf Baechle 
1608736fad17SKay Sievers 	device_del(&vpe_device);
1609e01402b1SRalf Baechle 	unregister_chrdev(major, module_name);
16101bbfc20dSRalf Baechle 
16111bbfc20dSRalf Baechle 	/* No locking needed here */
16121bbfc20dSRalf Baechle 	list_for_each_entry_safe(v, n, &vpecontrol.vpe_list, list) {
16131bbfc20dSRalf Baechle 		if (v->state != VPE_STATE_UNUSED)
16141bbfc20dSRalf Baechle 			release_vpe(v);
16151bbfc20dSRalf Baechle 	}
1616e01402b1SRalf Baechle }
1617e01402b1SRalf Baechle 
1618e01402b1SRalf Baechle module_init(vpe_module_init);
1619e01402b1SRalf Baechle module_exit(vpe_module_exit);
1620e01402b1SRalf Baechle MODULE_DESCRIPTION("MIPS VPE Loader");
16212600990eSRalf Baechle MODULE_AUTHOR("Elizabeth Oldham, MIPS Technologies, Inc.");
1622e01402b1SRalf Baechle MODULE_LICENSE("GPL");
1623