xref: /openbmc/linux/arch/mips/kernel/vpe.c (revision f18b51cc)
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>
417558da94SJonathan Corbet #include <linux/smp_lock.h>
42e01402b1SRalf Baechle #include <linux/syscalls.h>
43e01402b1SRalf Baechle #include <linux/moduleloader.h>
44e01402b1SRalf Baechle #include <linux/interrupt.h>
45e01402b1SRalf Baechle #include <linux/poll.h>
46e01402b1SRalf Baechle #include <linux/bootmem.h>
47e01402b1SRalf Baechle #include <asm/mipsregs.h>
48340ee4b9SRalf Baechle #include <asm/mipsmtregs.h>
49e01402b1SRalf Baechle #include <asm/cacheflush.h>
50e01402b1SRalf Baechle #include <asm/atomic.h>
51e01402b1SRalf Baechle #include <asm/cpu.h>
5227a3bbafSRalf Baechle #include <asm/mips_mt.h>
53e01402b1SRalf Baechle #include <asm/processor.h>
54e01402b1SRalf Baechle #include <asm/system.h>
552600990eSRalf Baechle #include <asm/vpe.h>
562600990eSRalf Baechle #include <asm/kspd.h>
57e01402b1SRalf Baechle 
58e01402b1SRalf Baechle typedef void *vpe_handle;
59e01402b1SRalf Baechle 
60e01402b1SRalf Baechle #ifndef ARCH_SHF_SMALL
61e01402b1SRalf Baechle #define ARCH_SHF_SMALL 0
62e01402b1SRalf Baechle #endif
63e01402b1SRalf Baechle 
64e01402b1SRalf Baechle /* If this is set, the section belongs in the init part of the module */
65e01402b1SRalf Baechle #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
66e01402b1SRalf Baechle 
6741790e04SRalf Baechle /*
6841790e04SRalf Baechle  * The number of TCs and VPEs physically available on the core
6941790e04SRalf Baechle  */
7041790e04SRalf Baechle static int hw_tcs, hw_vpes;
71e01402b1SRalf Baechle static char module_name[] = "vpe";
72307bd284SRalf Baechle static int major;
7327a3bbafSRalf Baechle static const int minor = 1;	/* fixed for now  */
74e01402b1SRalf Baechle 
752600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
762600990eSRalf Baechle  static struct kspd_notifications kspd_events;
772600990eSRalf Baechle static int kspd_events_reqd = 0;
782600990eSRalf Baechle #endif
792600990eSRalf Baechle 
80e01402b1SRalf Baechle /* grab the likely amount of memory we will need. */
81e01402b1SRalf Baechle #ifdef CONFIG_MIPS_VPE_LOADER_TOM
82e01402b1SRalf Baechle #define P_SIZE (2 * 1024 * 1024)
83e01402b1SRalf Baechle #else
84e01402b1SRalf Baechle /* add an overhead to the max kmalloc size for non-striped symbols/etc */
85e01402b1SRalf Baechle #define P_SIZE (256 * 1024)
86e01402b1SRalf Baechle #endif
87e01402b1SRalf Baechle 
882600990eSRalf Baechle extern unsigned long physical_memsize;
892600990eSRalf Baechle 
90e01402b1SRalf Baechle #define MAX_VPES 16
912600990eSRalf Baechle #define VPE_PATH_MAX 256
92e01402b1SRalf Baechle 
93e01402b1SRalf Baechle enum vpe_state {
94e01402b1SRalf Baechle 	VPE_STATE_UNUSED = 0,
95e01402b1SRalf Baechle 	VPE_STATE_INUSE,
96e01402b1SRalf Baechle 	VPE_STATE_RUNNING
97e01402b1SRalf Baechle };
98e01402b1SRalf Baechle 
99e01402b1SRalf Baechle enum tc_state {
100e01402b1SRalf Baechle 	TC_STATE_UNUSED = 0,
101e01402b1SRalf Baechle 	TC_STATE_INUSE,
102e01402b1SRalf Baechle 	TC_STATE_RUNNING,
103e01402b1SRalf Baechle 	TC_STATE_DYNAMIC
104e01402b1SRalf Baechle };
105e01402b1SRalf Baechle 
106307bd284SRalf Baechle struct vpe {
107e01402b1SRalf Baechle 	enum vpe_state state;
108e01402b1SRalf Baechle 
109e01402b1SRalf Baechle 	/* (device) minor associated with this vpe */
110e01402b1SRalf Baechle 	int minor;
111e01402b1SRalf Baechle 
112e01402b1SRalf Baechle 	/* elfloader stuff */
113e01402b1SRalf Baechle 	void *load_addr;
114571e0bedSRalf Baechle 	unsigned long len;
115e01402b1SRalf Baechle 	char *pbuffer;
116571e0bedSRalf Baechle 	unsigned long plen;
1172600990eSRalf Baechle 	unsigned int uid, gid;
1182600990eSRalf Baechle 	char cwd[VPE_PATH_MAX];
119e01402b1SRalf Baechle 
120e01402b1SRalf Baechle 	unsigned long __start;
121e01402b1SRalf Baechle 
122e01402b1SRalf Baechle 	/* tc's associated with this vpe */
123e01402b1SRalf Baechle 	struct list_head tc;
124e01402b1SRalf Baechle 
125e01402b1SRalf Baechle 	/* The list of vpe's */
126e01402b1SRalf Baechle 	struct list_head list;
127e01402b1SRalf Baechle 
128e01402b1SRalf Baechle 	/* shared symbol address */
129e01402b1SRalf Baechle 	void *shared_ptr;
1302600990eSRalf Baechle 
1312600990eSRalf Baechle 	/* the list of who wants to know when something major happens */
1322600990eSRalf Baechle 	struct list_head notify;
13341790e04SRalf Baechle 
13441790e04SRalf Baechle 	unsigned int ntcs;
135307bd284SRalf Baechle };
136307bd284SRalf Baechle 
137307bd284SRalf Baechle struct tc {
138307bd284SRalf Baechle 	enum tc_state state;
139307bd284SRalf Baechle 	int index;
140307bd284SRalf Baechle 
14107cc0c9eSRalf Baechle 	struct vpe *pvpe;	/* parent VPE */
14207cc0c9eSRalf Baechle 	struct list_head tc;	/* The list of TC's with this VPE */
14307cc0c9eSRalf Baechle 	struct list_head list;	/* The global list of tc's */
144307bd284SRalf Baechle };
145e01402b1SRalf Baechle 
1469cfdf6f1SRalf Baechle struct {
147e01402b1SRalf Baechle 	/* Virtual processing elements */
148e01402b1SRalf Baechle 	struct list_head vpe_list;
149e01402b1SRalf Baechle 
150e01402b1SRalf Baechle 	/* Thread contexts */
151e01402b1SRalf Baechle 	struct list_head tc_list;
1529cfdf6f1SRalf Baechle } vpecontrol = {
1539cfdf6f1SRalf Baechle 	.vpe_list = LIST_HEAD_INIT(vpecontrol.vpe_list),
1549cfdf6f1SRalf Baechle 	.tc_list = LIST_HEAD_INIT(vpecontrol.tc_list)
1559cfdf6f1SRalf Baechle };
156e01402b1SRalf Baechle 
157e01402b1SRalf Baechle static void release_progmem(void *ptr);
158e01402b1SRalf Baechle 
159e01402b1SRalf Baechle /* get the vpe associated with this minor */
160f18b51ccSRalf Baechle static 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 */
176f18b51ccSRalf Baechle static 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) */
201f18b51ccSRalf Baechle static 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. */
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;
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 */
234f18b51ccSRalf Baechle static 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 
242f18b51ccSRalf Baechle static 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 	 */
2729f2546adSRalf Baechle 	addr = pfn_to_kaddr(max_low_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;
329e2a9cf96SRaghu Gandham 			s->sh_entsize =
330e2a9cf96SRaghu Gandham 				get_offset((unsigned long *)&mod->core_size, s);
331e01402b1SRalf Baechle 		}
332e01402b1SRalf Baechle 
333e01402b1SRalf Baechle 		if (m == 0)
334e01402b1SRalf Baechle 			mod->core_text_size = mod->core_size;
335e01402b1SRalf Baechle 
336e01402b1SRalf Baechle 	}
337e01402b1SRalf Baechle }
338e01402b1SRalf Baechle 
339e01402b1SRalf Baechle 
340e01402b1SRalf Baechle /* from module-elf32.c, but subverted a little */
341e01402b1SRalf Baechle 
342e01402b1SRalf Baechle struct mips_hi16 {
343e01402b1SRalf Baechle 	struct mips_hi16 *next;
344e01402b1SRalf Baechle 	Elf32_Addr *addr;
345e01402b1SRalf Baechle 	Elf32_Addr value;
346e01402b1SRalf Baechle };
347e01402b1SRalf Baechle 
348e01402b1SRalf Baechle static struct mips_hi16 *mips_hi16_list;
349e01402b1SRalf Baechle static unsigned int gp_offs, gp_addr;
350e01402b1SRalf Baechle 
351e01402b1SRalf Baechle static int apply_r_mips_none(struct module *me, uint32_t *location,
352e01402b1SRalf Baechle 			     Elf32_Addr v)
353e01402b1SRalf Baechle {
354e01402b1SRalf Baechle 	return 0;
355e01402b1SRalf Baechle }
356e01402b1SRalf Baechle 
357e01402b1SRalf Baechle static int apply_r_mips_gprel16(struct module *me, uint32_t *location,
358e01402b1SRalf Baechle 				Elf32_Addr v)
359e01402b1SRalf Baechle {
360e01402b1SRalf Baechle 	int rel;
361e01402b1SRalf Baechle 
362e01402b1SRalf Baechle 	if( !(*location & 0xffff) ) {
363e01402b1SRalf Baechle 		rel = (int)v - gp_addr;
364e01402b1SRalf Baechle 	}
365e01402b1SRalf Baechle 	else {
366e01402b1SRalf Baechle 		/* .sbss + gp(relative) + offset */
367e01402b1SRalf Baechle 		/* kludge! */
368e01402b1SRalf Baechle 		rel =  (int)(short)((int)v + gp_offs +
369e01402b1SRalf Baechle 				    (int)(short)(*location & 0xffff) - gp_addr);
370e01402b1SRalf Baechle 	}
371e01402b1SRalf Baechle 
372e01402b1SRalf Baechle 	if( (rel > 32768) || (rel < -32768) ) {
3732600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: apply_r_mips_gprel16: "
3742600990eSRalf Baechle 		       "relative address 0x%x out of range of gp register\n",
3752600990eSRalf Baechle 		       rel);
376e01402b1SRalf Baechle 		return -ENOEXEC;
377e01402b1SRalf Baechle 	}
378e01402b1SRalf Baechle 
379e01402b1SRalf Baechle 	*location = (*location & 0xffff0000) | (rel & 0xffff);
380e01402b1SRalf Baechle 
381e01402b1SRalf Baechle 	return 0;
382e01402b1SRalf Baechle }
383e01402b1SRalf Baechle 
384e01402b1SRalf Baechle static int apply_r_mips_pc16(struct module *me, uint32_t *location,
385e01402b1SRalf Baechle 			     Elf32_Addr v)
386e01402b1SRalf Baechle {
387e01402b1SRalf Baechle 	int rel;
388e01402b1SRalf Baechle 	rel = (((unsigned int)v - (unsigned int)location));
389e01402b1SRalf Baechle 	rel >>= 2;		// because the offset is in _instructions_ not bytes.
390e01402b1SRalf Baechle 	rel -= 1;		// and one instruction less due to the branch delay slot.
391e01402b1SRalf Baechle 
392e01402b1SRalf Baechle 	if( (rel > 32768) || (rel < -32768) ) {
3932600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: "
394e01402b1SRalf Baechle  		       "apply_r_mips_pc16: relative address out of range 0x%x\n", rel);
395e01402b1SRalf Baechle 		return -ENOEXEC;
396e01402b1SRalf Baechle 	}
397e01402b1SRalf Baechle 
398e01402b1SRalf Baechle 	*location = (*location & 0xffff0000) | (rel & 0xffff);
399e01402b1SRalf Baechle 
400e01402b1SRalf Baechle 	return 0;
401e01402b1SRalf Baechle }
402e01402b1SRalf Baechle 
403e01402b1SRalf Baechle static int apply_r_mips_32(struct module *me, uint32_t *location,
404e01402b1SRalf Baechle 			   Elf32_Addr v)
405e01402b1SRalf Baechle {
406e01402b1SRalf Baechle 	*location += v;
407e01402b1SRalf Baechle 
408e01402b1SRalf Baechle 	return 0;
409e01402b1SRalf Baechle }
410e01402b1SRalf Baechle 
411e01402b1SRalf Baechle static int apply_r_mips_26(struct module *me, uint32_t *location,
412e01402b1SRalf Baechle 			   Elf32_Addr v)
413e01402b1SRalf Baechle {
414e01402b1SRalf Baechle 	if (v % 4) {
4152600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: apply_r_mips_26 "
4162600990eSRalf Baechle 		       " unaligned relocation\n");
417e01402b1SRalf Baechle 		return -ENOEXEC;
418e01402b1SRalf Baechle 	}
419e01402b1SRalf Baechle 
420307bd284SRalf Baechle /*
421307bd284SRalf Baechle  * Not desperately convinced this is a good check of an overflow condition
422307bd284SRalf Baechle  * anyway. But it gets in the way of handling undefined weak symbols which
423307bd284SRalf Baechle  * we want to set to zero.
424307bd284SRalf Baechle  * if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) {
425307bd284SRalf Baechle  * printk(KERN_ERR
426307bd284SRalf Baechle  * "module %s: relocation overflow\n",
427307bd284SRalf Baechle  * me->name);
428307bd284SRalf Baechle  * return -ENOEXEC;
429307bd284SRalf Baechle  * }
430e01402b1SRalf Baechle  */
431e01402b1SRalf Baechle 
432e01402b1SRalf Baechle 	*location = (*location & ~0x03ffffff) |
433e01402b1SRalf Baechle 		((*location + (v >> 2)) & 0x03ffffff);
434e01402b1SRalf Baechle 	return 0;
435e01402b1SRalf Baechle }
436e01402b1SRalf Baechle 
437e01402b1SRalf Baechle static int apply_r_mips_hi16(struct module *me, uint32_t *location,
438e01402b1SRalf Baechle 			     Elf32_Addr v)
439e01402b1SRalf Baechle {
440e01402b1SRalf Baechle 	struct mips_hi16 *n;
441e01402b1SRalf Baechle 
442e01402b1SRalf Baechle 	/*
443e01402b1SRalf Baechle 	 * We cannot relocate this one now because we don't know the value of
444e01402b1SRalf Baechle 	 * the carry we need to add.  Save the information, and let LO16 do the
445e01402b1SRalf Baechle 	 * actual relocation.
446e01402b1SRalf Baechle 	 */
447e01402b1SRalf Baechle 	n = kmalloc(sizeof *n, GFP_KERNEL);
448e01402b1SRalf Baechle 	if (!n)
449e01402b1SRalf Baechle 		return -ENOMEM;
450e01402b1SRalf Baechle 
451e01402b1SRalf Baechle 	n->addr = location;
452e01402b1SRalf Baechle 	n->value = v;
453e01402b1SRalf Baechle 	n->next = mips_hi16_list;
454e01402b1SRalf Baechle 	mips_hi16_list = n;
455e01402b1SRalf Baechle 
456e01402b1SRalf Baechle 	return 0;
457e01402b1SRalf Baechle }
458e01402b1SRalf Baechle 
459e01402b1SRalf Baechle static int apply_r_mips_lo16(struct module *me, uint32_t *location,
460e01402b1SRalf Baechle 			     Elf32_Addr v)
461e01402b1SRalf Baechle {
462e01402b1SRalf Baechle 	unsigned long insnlo = *location;
463e01402b1SRalf Baechle 	Elf32_Addr val, vallo;
464477c4b07SRalf Baechle 	struct mips_hi16 *l, *next;
465e01402b1SRalf Baechle 
466e01402b1SRalf Baechle 	/* Sign extend the addend we extract from the lo insn.  */
467e01402b1SRalf Baechle 	vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;
468e01402b1SRalf Baechle 
469e01402b1SRalf Baechle 	if (mips_hi16_list != NULL) {
470e01402b1SRalf Baechle 
471e01402b1SRalf Baechle 		l = mips_hi16_list;
472e01402b1SRalf Baechle 		while (l != NULL) {
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");
482477c4b07SRalf Baechle 				goto out_free;
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;
520477c4b07SRalf Baechle 
521477c4b07SRalf Baechle out_free:
522477c4b07SRalf Baechle 	while (l != NULL) {
523477c4b07SRalf Baechle 		next = l->next;
524477c4b07SRalf Baechle 		kfree(l);
525477c4b07SRalf Baechle 		l = next;
526477c4b07SRalf Baechle 	}
527477c4b07SRalf Baechle 	mips_hi16_list = NULL;
528477c4b07SRalf Baechle 
529477c4b07SRalf Baechle 	return -ENOEXEC;
530e01402b1SRalf Baechle }
531e01402b1SRalf Baechle 
532e01402b1SRalf Baechle static int (*reloc_handlers[]) (struct module *me, uint32_t *location,
533e01402b1SRalf Baechle 				Elf32_Addr v) = {
534e01402b1SRalf Baechle 	[R_MIPS_NONE]	= apply_r_mips_none,
535e01402b1SRalf Baechle 	[R_MIPS_32]	= apply_r_mips_32,
536e01402b1SRalf Baechle 	[R_MIPS_26]	= apply_r_mips_26,
537e01402b1SRalf Baechle 	[R_MIPS_HI16]	= apply_r_mips_hi16,
538e01402b1SRalf Baechle 	[R_MIPS_LO16]	= apply_r_mips_lo16,
539e01402b1SRalf Baechle 	[R_MIPS_GPREL16] = apply_r_mips_gprel16,
540e01402b1SRalf Baechle 	[R_MIPS_PC16] = apply_r_mips_pc16
541e01402b1SRalf Baechle };
542e01402b1SRalf Baechle 
5432600990eSRalf Baechle static char *rstrs[] = {
5442600990eSRalf Baechle 	[R_MIPS_NONE]	= "MIPS_NONE",
5452600990eSRalf Baechle 	[R_MIPS_32]	= "MIPS_32",
5462600990eSRalf Baechle 	[R_MIPS_26]	= "MIPS_26",
5472600990eSRalf Baechle 	[R_MIPS_HI16]	= "MIPS_HI16",
5482600990eSRalf Baechle 	[R_MIPS_LO16]	= "MIPS_LO16",
5492600990eSRalf Baechle 	[R_MIPS_GPREL16] = "MIPS_GPREL16",
5502600990eSRalf Baechle 	[R_MIPS_PC16] = "MIPS_PC16"
5512600990eSRalf Baechle };
552e01402b1SRalf Baechle 
553f18b51ccSRalf Baechle static int apply_relocations(Elf32_Shdr *sechdrs,
554e01402b1SRalf Baechle 		      const char *strtab,
555e01402b1SRalf Baechle 		      unsigned int symindex,
556e01402b1SRalf Baechle 		      unsigned int relsec,
557e01402b1SRalf Baechle 		      struct module *me)
558e01402b1SRalf Baechle {
559e01402b1SRalf Baechle 	Elf32_Rel *rel = (void *) sechdrs[relsec].sh_addr;
560e01402b1SRalf Baechle 	Elf32_Sym *sym;
561e01402b1SRalf Baechle 	uint32_t *location;
562e01402b1SRalf Baechle 	unsigned int i;
563e01402b1SRalf Baechle 	Elf32_Addr v;
564e01402b1SRalf Baechle 	int res;
565e01402b1SRalf Baechle 
566e01402b1SRalf Baechle 	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
567e01402b1SRalf Baechle 		Elf32_Word r_info = rel[i].r_info;
568e01402b1SRalf Baechle 
569e01402b1SRalf Baechle 		/* This is where to make the change */
570e01402b1SRalf Baechle 		location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
571e01402b1SRalf Baechle 			+ rel[i].r_offset;
572e01402b1SRalf Baechle 		/* This is the symbol it is referring to */
573e01402b1SRalf Baechle 		sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
574e01402b1SRalf Baechle 			+ ELF32_R_SYM(r_info);
575e01402b1SRalf Baechle 
576e01402b1SRalf Baechle 		if (!sym->st_value) {
577e01402b1SRalf Baechle 			printk(KERN_DEBUG "%s: undefined weak symbol %s\n",
578e01402b1SRalf Baechle 			       me->name, strtab + sym->st_name);
579e01402b1SRalf Baechle 			/* just print the warning, dont barf */
580e01402b1SRalf Baechle 		}
581e01402b1SRalf Baechle 
582e01402b1SRalf Baechle 		v = sym->st_value;
583e01402b1SRalf Baechle 
584e01402b1SRalf Baechle 		res = reloc_handlers[ELF32_R_TYPE(r_info)](me, location, v);
585e01402b1SRalf Baechle 		if( res ) {
5862600990eSRalf Baechle 			char *r = rstrs[ELF32_R_TYPE(r_info)];
5872600990eSRalf Baechle 		    	printk(KERN_WARNING "VPE loader: .text+0x%x "
5882600990eSRalf Baechle 			       "relocation type %s for symbol \"%s\" failed\n",
5892600990eSRalf Baechle 			       rel[i].r_offset, r ? r : "UNKNOWN",
5902600990eSRalf Baechle 			       strtab + sym->st_name);
591e01402b1SRalf Baechle 			return res;
592e01402b1SRalf Baechle 		}
5932600990eSRalf Baechle 	}
594e01402b1SRalf Baechle 
595e01402b1SRalf Baechle 	return 0;
596e01402b1SRalf Baechle }
597e01402b1SRalf Baechle 
598f18b51ccSRalf Baechle static inline void save_gp_address(unsigned int secbase, unsigned int rel)
599e01402b1SRalf Baechle {
600e01402b1SRalf Baechle 	gp_addr = secbase + rel;
601e01402b1SRalf Baechle 	gp_offs = gp_addr - (secbase & 0xffff0000);
602e01402b1SRalf Baechle }
603e01402b1SRalf Baechle /* end module-elf32.c */
604e01402b1SRalf Baechle 
605e01402b1SRalf Baechle 
606e01402b1SRalf Baechle 
607e01402b1SRalf Baechle /* Change all symbols so that sh_value encodes the pointer directly. */
6082600990eSRalf Baechle static void simplify_symbols(Elf_Shdr * sechdrs,
609e01402b1SRalf Baechle 			    unsigned int symindex,
610e01402b1SRalf Baechle 			    const char *strtab,
611e01402b1SRalf Baechle 			    const char *secstrings,
612e01402b1SRalf Baechle 			    unsigned int nsecs, struct module *mod)
613e01402b1SRalf Baechle {
614e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
615e01402b1SRalf Baechle 	unsigned long secbase, bssbase = 0;
616e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
6172600990eSRalf Baechle 	int size;
618e01402b1SRalf Baechle 
619e01402b1SRalf Baechle 	/* find the .bss section for COMMON symbols */
620e01402b1SRalf Baechle 	for (i = 0; i < nsecs; i++) {
6212600990eSRalf Baechle 		if (strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) == 0) {
622e01402b1SRalf Baechle 			bssbase = sechdrs[i].sh_addr;
6232600990eSRalf Baechle 			break;
6242600990eSRalf Baechle 		}
625e01402b1SRalf Baechle 	}
626e01402b1SRalf Baechle 
627e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
628e01402b1SRalf Baechle 		switch (sym[i].st_shndx) {
629e01402b1SRalf Baechle 		case SHN_COMMON:
6302600990eSRalf Baechle 			/* Allocate space for the symbol in the .bss section.
6312600990eSRalf Baechle 			   st_value is currently size.
632e01402b1SRalf Baechle 			   We want it to have the address of the symbol. */
633e01402b1SRalf Baechle 
634e01402b1SRalf Baechle 			size = sym[i].st_value;
635e01402b1SRalf Baechle 			sym[i].st_value = bssbase;
636e01402b1SRalf Baechle 
637e01402b1SRalf Baechle 			bssbase += size;
638e01402b1SRalf Baechle 			break;
639e01402b1SRalf Baechle 
640e01402b1SRalf Baechle 		case SHN_ABS:
641e01402b1SRalf Baechle 			/* Don't need to do anything */
642e01402b1SRalf Baechle 			break;
643e01402b1SRalf Baechle 
644e01402b1SRalf Baechle 		case SHN_UNDEF:
645e01402b1SRalf Baechle 			/* ret = -ENOENT; */
646e01402b1SRalf Baechle 			break;
647e01402b1SRalf Baechle 
648e01402b1SRalf Baechle 		case SHN_MIPS_SCOMMON:
6492600990eSRalf Baechle 			printk(KERN_DEBUG "simplify_symbols: ignoring SHN_MIPS_SCOMMON "
6502600990eSRalf Baechle 			       "symbol <%s> st_shndx %d\n", strtab + sym[i].st_name,
6512600990eSRalf Baechle 			       sym[i].st_shndx);
652e01402b1SRalf Baechle 			// .sbss section
653e01402b1SRalf Baechle 			break;
654e01402b1SRalf Baechle 
655e01402b1SRalf Baechle 		default:
656e01402b1SRalf Baechle 			secbase = sechdrs[sym[i].st_shndx].sh_addr;
657e01402b1SRalf Baechle 
658e01402b1SRalf Baechle 			if (strncmp(strtab + sym[i].st_name, "_gp", 3) == 0) {
659e01402b1SRalf Baechle 				save_gp_address(secbase, sym[i].st_value);
660e01402b1SRalf Baechle 			}
661e01402b1SRalf Baechle 
662e01402b1SRalf Baechle 			sym[i].st_value += secbase;
663e01402b1SRalf Baechle 			break;
664e01402b1SRalf Baechle 		}
665e01402b1SRalf Baechle 	}
666e01402b1SRalf Baechle }
667e01402b1SRalf Baechle 
668e01402b1SRalf Baechle #ifdef DEBUG_ELFLOADER
669e01402b1SRalf Baechle static void dump_elfsymbols(Elf_Shdr * sechdrs, unsigned int symindex,
670e01402b1SRalf Baechle 			    const char *strtab, struct module *mod)
671e01402b1SRalf Baechle {
672e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
673e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
674e01402b1SRalf Baechle 
675e01402b1SRalf Baechle 	printk(KERN_DEBUG "dump_elfsymbols: n %d\n", n);
676e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
677e01402b1SRalf Baechle 		printk(KERN_DEBUG " i %d name <%s> 0x%x\n", i,
678e01402b1SRalf Baechle 		       strtab + sym[i].st_name, sym[i].st_value);
679e01402b1SRalf Baechle 	}
680e01402b1SRalf Baechle }
681e01402b1SRalf Baechle #endif
682e01402b1SRalf Baechle 
683e01402b1SRalf Baechle /* We are prepared so configure and start the VPE... */
684be6e1437SRalf Baechle static int vpe_run(struct vpe * v)
685e01402b1SRalf Baechle {
68607cc0c9eSRalf Baechle 	unsigned long flags, val, dmt_flag;
6872600990eSRalf Baechle 	struct vpe_notifications *n;
68807cc0c9eSRalf Baechle 	unsigned int vpeflags;
689e01402b1SRalf Baechle 	struct tc *t;
690e01402b1SRalf Baechle 
691e01402b1SRalf Baechle 	/* check we are the Master VPE */
69207cc0c9eSRalf Baechle 	local_irq_save(flags);
693e01402b1SRalf Baechle 	val = read_c0_vpeconf0();
694e01402b1SRalf Baechle 	if (!(val & VPECONF0_MVP)) {
695e01402b1SRalf Baechle 		printk(KERN_WARNING
6962600990eSRalf Baechle 		       "VPE loader: only Master VPE's are allowed to configure MT\n");
69707cc0c9eSRalf Baechle 		local_irq_restore(flags);
69807cc0c9eSRalf Baechle 
699e01402b1SRalf Baechle 		return -1;
700e01402b1SRalf Baechle 	}
701e01402b1SRalf Baechle 
70207cc0c9eSRalf Baechle 	dmt_flag = dmt();
70307cc0c9eSRalf Baechle 	vpeflags = dvpe();
704e01402b1SRalf Baechle 
705e01402b1SRalf Baechle 	if (!list_empty(&v->tc)) {
706e01402b1SRalf Baechle 		if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
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: TC %d is already in use.\n",
713e01402b1SRalf Baechle                                t->index);
714e01402b1SRalf Baechle 			return -ENOEXEC;
715e01402b1SRalf Baechle 		}
716e01402b1SRalf Baechle 	} else {
71707cc0c9eSRalf Baechle 		evpe(vpeflags);
71807cc0c9eSRalf Baechle 		emt(dmt_flag);
71907cc0c9eSRalf Baechle 		local_irq_restore(flags);
72007cc0c9eSRalf Baechle 
72107cc0c9eSRalf Baechle 		printk(KERN_WARNING
72207cc0c9eSRalf Baechle 		       "VPE loader: No TC's associated with VPE %d\n",
723e01402b1SRalf Baechle 		       v->minor);
72407cc0c9eSRalf Baechle 
725e01402b1SRalf Baechle 		return -ENOEXEC;
726e01402b1SRalf Baechle 	}
727e01402b1SRalf Baechle 
7282600990eSRalf Baechle 	/* Put MVPE's into 'configuration state' */
7292600990eSRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
730e01402b1SRalf Baechle 
7312600990eSRalf Baechle 	settc(t->index);
732e01402b1SRalf Baechle 
733e01402b1SRalf Baechle 	/* should check it is halted, and not activated */
734e01402b1SRalf Baechle 	if ((read_tc_c0_tcstatus() & TCSTATUS_A) || !(read_tc_c0_tchalt() & TCHALT_H)) {
73507cc0c9eSRalf Baechle 		evpe(vpeflags);
73607cc0c9eSRalf Baechle 		emt(dmt_flag);
73707cc0c9eSRalf Baechle 		local_irq_restore(flags);
73807cc0c9eSRalf Baechle 
73907cc0c9eSRalf Baechle 		printk(KERN_WARNING "VPE loader: TC %d is already active!\n",
740e01402b1SRalf Baechle 		       t->index);
74107cc0c9eSRalf Baechle 
742e01402b1SRalf Baechle 		return -ENOEXEC;
743e01402b1SRalf Baechle 	}
744e01402b1SRalf Baechle 
745e01402b1SRalf Baechle 	/* Write the address we want it to start running from in the TCPC register. */
746e01402b1SRalf Baechle 	write_tc_c0_tcrestart((unsigned long)v->__start);
747e01402b1SRalf Baechle 	write_tc_c0_tccontext((unsigned long)0);
74807cc0c9eSRalf Baechle 
7492600990eSRalf Baechle 	/*
7502600990eSRalf Baechle 	 * Mark the TC as activated, not interrupt exempt and not dynamically
7512600990eSRalf Baechle 	 * allocatable
7522600990eSRalf Baechle 	 */
753e01402b1SRalf Baechle 	val = read_tc_c0_tcstatus();
754e01402b1SRalf Baechle 	val = (val & ~(TCSTATUS_DA | TCSTATUS_IXMT)) | TCSTATUS_A;
755e01402b1SRalf Baechle 	write_tc_c0_tcstatus(val);
756e01402b1SRalf Baechle 
757e01402b1SRalf Baechle 	write_tc_c0_tchalt(read_tc_c0_tchalt() & ~TCHALT_H);
758e01402b1SRalf Baechle 
759e01402b1SRalf Baechle 	/*
760e01402b1SRalf Baechle 	 * The sde-kit passes 'memsize' to __start in $a3, so set something
7612600990eSRalf Baechle 	 * here...  Or set $a3 to zero and define DFLT_STACK_SIZE and
762e01402b1SRalf Baechle 	 * DFLT_HEAP_SIZE when you compile your program
763e01402b1SRalf Baechle 	 */
76441790e04SRalf Baechle 	mttgpr(6, v->ntcs);
7652600990eSRalf Baechle 	mttgpr(7, physical_memsize);
766e01402b1SRalf Baechle 
7672600990eSRalf Baechle 	/* set up VPE1 */
7682600990eSRalf Baechle 	/*
7692600990eSRalf Baechle 	 * bind the TC to VPE 1 as late as possible so we only have the final
7702600990eSRalf Baechle 	 * VPE registers to set up, and so an EJTAG probe can trigger on it
7712600990eSRalf Baechle 	 */
77207cc0c9eSRalf Baechle 	write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | 1);
7732600990eSRalf Baechle 
774a94d7020SElizabeth Oldham 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~(VPECONF0_VPA));
775a94d7020SElizabeth Oldham 
776a94d7020SElizabeth Oldham 	back_to_back_c0_hazard();
777a94d7020SElizabeth Oldham 
7782600990eSRalf Baechle 	/* Set up the XTC bit in vpeconf0 to point at our tc */
7792600990eSRalf Baechle 	write_vpe_c0_vpeconf0( (read_vpe_c0_vpeconf0() & ~(VPECONF0_XTC))
7802600990eSRalf Baechle 	                      | (t->index << VPECONF0_XTC_SHIFT));
7812600990eSRalf Baechle 
782a94d7020SElizabeth Oldham 	back_to_back_c0_hazard();
783a94d7020SElizabeth Oldham 
7842600990eSRalf Baechle 	/* enable this VPE */
7852600990eSRalf Baechle 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA);
786e01402b1SRalf Baechle 
787e01402b1SRalf Baechle 	/* clear out any left overs from a previous program */
7882600990eSRalf Baechle 	write_vpe_c0_status(0);
789e01402b1SRalf Baechle 	write_vpe_c0_cause(0);
790e01402b1SRalf Baechle 
791e01402b1SRalf Baechle 	/* take system out of configuration state */
792340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
793e01402b1SRalf Baechle 
794b618336aSKevin D. Kissell 	/*
795b618336aSKevin D. Kissell 	 * SMTC/SMVP kernels manage VPE enable independently,
796b618336aSKevin D. Kissell 	 * but uniprocessor kernels need to turn it on, even
797b618336aSKevin D. Kissell 	 * if that wasn't the pre-dvpe() state.
798b618336aSKevin D. Kissell 	 */
79907cc0c9eSRalf Baechle #ifdef CONFIG_SMP
80007cc0c9eSRalf Baechle 	evpe(vpeflags);
801b618336aSKevin D. Kissell #else
802b618336aSKevin D. Kissell 	evpe(EVPE_ENABLE);
80307cc0c9eSRalf Baechle #endif
80407cc0c9eSRalf Baechle 	emt(dmt_flag);
80507cc0c9eSRalf Baechle 	local_irq_restore(flags);
806e01402b1SRalf Baechle 
80707cc0c9eSRalf Baechle 	list_for_each_entry(n, &v->notify, list)
80807cc0c9eSRalf Baechle 		n->start(minor);
8092600990eSRalf Baechle 
810e01402b1SRalf Baechle 	return 0;
811e01402b1SRalf Baechle }
812e01402b1SRalf Baechle 
8132600990eSRalf Baechle static int find_vpe_symbols(struct vpe * v, Elf_Shdr * sechdrs,
814e01402b1SRalf Baechle 				      unsigned int symindex, const char *strtab,
815e01402b1SRalf Baechle 				      struct module *mod)
816e01402b1SRalf Baechle {
817e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
818e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
819e01402b1SRalf Baechle 
820e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
821e01402b1SRalf Baechle 		if (strcmp(strtab + sym[i].st_name, "__start") == 0) {
822e01402b1SRalf Baechle 			v->__start = sym[i].st_value;
823e01402b1SRalf Baechle 		}
824e01402b1SRalf Baechle 
825e01402b1SRalf Baechle 		if (strcmp(strtab + sym[i].st_name, "vpe_shared") == 0) {
826e01402b1SRalf Baechle 			v->shared_ptr = (void *)sym[i].st_value;
827e01402b1SRalf Baechle 		}
828e01402b1SRalf Baechle 	}
829e01402b1SRalf Baechle 
8302600990eSRalf Baechle 	if ( (v->__start == 0) || (v->shared_ptr == NULL))
8312600990eSRalf Baechle 		return -1;
8322600990eSRalf Baechle 
833e01402b1SRalf Baechle 	return 0;
834e01402b1SRalf Baechle }
835e01402b1SRalf Baechle 
836307bd284SRalf Baechle /*
8372600990eSRalf Baechle  * Allocates a VPE with some program code space(the load address), copies the
8382600990eSRalf Baechle  * contents of the program (p)buffer performing relocatations/etc, free's it
8392600990eSRalf Baechle  * when finished.
840e01402b1SRalf Baechle  */
841be6e1437SRalf Baechle static int vpe_elfload(struct vpe * v)
842e01402b1SRalf Baechle {
843e01402b1SRalf Baechle 	Elf_Ehdr *hdr;
844e01402b1SRalf Baechle 	Elf_Shdr *sechdrs;
845e01402b1SRalf Baechle 	long err = 0;
846e01402b1SRalf Baechle 	char *secstrings, *strtab = NULL;
8472600990eSRalf Baechle 	unsigned int len, i, symindex = 0, strindex = 0, relocate = 0;
848e01402b1SRalf Baechle 	struct module mod;	// so we can re-use the relocations code
849e01402b1SRalf Baechle 
850e01402b1SRalf Baechle 	memset(&mod, 0, sizeof(struct module));
8512600990eSRalf Baechle 	strcpy(mod.name, "VPE loader");
852e01402b1SRalf Baechle 
853e01402b1SRalf Baechle 	hdr = (Elf_Ehdr *) v->pbuffer;
854e01402b1SRalf Baechle 	len = v->plen;
855e01402b1SRalf Baechle 
856e01402b1SRalf Baechle 	/* Sanity checks against insmoding binaries or wrong arch,
857e01402b1SRalf Baechle 	   weird elf version */
858d303f4a1SCyrill Gorcunov 	if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0
8592600990eSRalf Baechle 	    || (hdr->e_type != ET_REL && hdr->e_type != ET_EXEC)
8602600990eSRalf Baechle 	    || !elf_check_arch(hdr)
861e01402b1SRalf Baechle 	    || hdr->e_shentsize != sizeof(*sechdrs)) {
862e01402b1SRalf Baechle 		printk(KERN_WARNING
8632600990eSRalf Baechle 		       "VPE loader: program wrong arch or weird elf version\n");
864e01402b1SRalf Baechle 
865e01402b1SRalf Baechle 		return -ENOEXEC;
866e01402b1SRalf Baechle 	}
867e01402b1SRalf Baechle 
8682600990eSRalf Baechle 	if (hdr->e_type == ET_REL)
8692600990eSRalf Baechle 		relocate = 1;
8702600990eSRalf Baechle 
871e01402b1SRalf Baechle 	if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) {
8722600990eSRalf Baechle 		printk(KERN_ERR "VPE loader: program length %u truncated\n",
8732600990eSRalf Baechle 		       len);
8742600990eSRalf Baechle 
875e01402b1SRalf Baechle 		return -ENOEXEC;
876e01402b1SRalf Baechle 	}
877e01402b1SRalf Baechle 
878e01402b1SRalf Baechle 	/* Convenience variables */
879e01402b1SRalf Baechle 	sechdrs = (void *)hdr + hdr->e_shoff;
880e01402b1SRalf Baechle 	secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
881e01402b1SRalf Baechle 	sechdrs[0].sh_addr = 0;
882e01402b1SRalf Baechle 
883e01402b1SRalf Baechle 	/* And these should exist, but gcc whinges if we don't init them */
884e01402b1SRalf Baechle 	symindex = strindex = 0;
885e01402b1SRalf Baechle 
8862600990eSRalf Baechle 	if (relocate) {
887e01402b1SRalf Baechle 		for (i = 1; i < hdr->e_shnum; i++) {
888e01402b1SRalf Baechle 			if (sechdrs[i].sh_type != SHT_NOBITS
889e01402b1SRalf Baechle 			    && len < sechdrs[i].sh_offset + sechdrs[i].sh_size) {
890e01402b1SRalf Baechle 				printk(KERN_ERR "VPE program length %u truncated\n",
891e01402b1SRalf Baechle 				       len);
892e01402b1SRalf Baechle 				return -ENOEXEC;
893e01402b1SRalf Baechle 			}
894e01402b1SRalf Baechle 
895e01402b1SRalf Baechle 			/* Mark all sections sh_addr with their address in the
896e01402b1SRalf Baechle 			   temporary image. */
897e01402b1SRalf Baechle 			sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
898e01402b1SRalf Baechle 
899e01402b1SRalf Baechle 			/* Internal symbols and strings. */
900e01402b1SRalf Baechle 			if (sechdrs[i].sh_type == SHT_SYMTAB) {
901e01402b1SRalf Baechle 				symindex = i;
902e01402b1SRalf Baechle 				strindex = sechdrs[i].sh_link;
903e01402b1SRalf Baechle 				strtab = (char *)hdr + sechdrs[strindex].sh_offset;
904e01402b1SRalf Baechle 			}
905e01402b1SRalf Baechle 		}
906e01402b1SRalf Baechle 		layout_sections(&mod, hdr, sechdrs, secstrings);
9072600990eSRalf Baechle 	}
908e01402b1SRalf Baechle 
909e01402b1SRalf Baechle 	v->load_addr = alloc_progmem(mod.core_size);
9105408c490SRalf Baechle 	if (!v->load_addr)
9115408c490SRalf Baechle 		return -ENOMEM;
912e01402b1SRalf Baechle 
9135408c490SRalf Baechle 	pr_info("VPE loader: loading to %p\n", v->load_addr);
914e01402b1SRalf Baechle 
9152600990eSRalf Baechle 	if (relocate) {
916e01402b1SRalf Baechle 		for (i = 0; i < hdr->e_shnum; i++) {
917e01402b1SRalf Baechle 			void *dest;
918e01402b1SRalf Baechle 
919e01402b1SRalf Baechle 			if (!(sechdrs[i].sh_flags & SHF_ALLOC))
920e01402b1SRalf Baechle 				continue;
921e01402b1SRalf Baechle 
922e01402b1SRalf Baechle 			dest = v->load_addr + sechdrs[i].sh_entsize;
923e01402b1SRalf Baechle 
924e01402b1SRalf Baechle 			if (sechdrs[i].sh_type != SHT_NOBITS)
925e01402b1SRalf Baechle 				memcpy(dest, (void *)sechdrs[i].sh_addr,
926e01402b1SRalf Baechle 				       sechdrs[i].sh_size);
927e01402b1SRalf Baechle 			/* Update sh_addr to point to copy in image. */
928e01402b1SRalf Baechle 			sechdrs[i].sh_addr = (unsigned long)dest;
9292600990eSRalf Baechle 
9302600990eSRalf Baechle 			printk(KERN_DEBUG " section sh_name %s sh_addr 0x%x\n",
9312600990eSRalf Baechle 			       secstrings + sechdrs[i].sh_name, sechdrs[i].sh_addr);
932e01402b1SRalf Baechle 		}
933e01402b1SRalf Baechle 
934e01402b1SRalf Baechle  		/* Fix up syms, so that st_value is a pointer to location. */
935e01402b1SRalf Baechle  		simplify_symbols(sechdrs, symindex, strtab, secstrings,
936e01402b1SRalf Baechle  				 hdr->e_shnum, &mod);
937e01402b1SRalf Baechle 
938e01402b1SRalf Baechle  		/* Now do relocations. */
939e01402b1SRalf Baechle  		for (i = 1; i < hdr->e_shnum; i++) {
940e01402b1SRalf Baechle  			const char *strtab = (char *)sechdrs[strindex].sh_addr;
941e01402b1SRalf Baechle  			unsigned int info = sechdrs[i].sh_info;
942e01402b1SRalf Baechle 
943e01402b1SRalf Baechle  			/* Not a valid relocation section? */
944e01402b1SRalf Baechle  			if (info >= hdr->e_shnum)
945e01402b1SRalf Baechle  				continue;
946e01402b1SRalf Baechle 
947e01402b1SRalf Baechle  			/* Don't bother with non-allocated sections */
948e01402b1SRalf Baechle  			if (!(sechdrs[info].sh_flags & SHF_ALLOC))
949e01402b1SRalf Baechle  				continue;
950e01402b1SRalf Baechle 
951e01402b1SRalf Baechle  			if (sechdrs[i].sh_type == SHT_REL)
9522600990eSRalf Baechle  				err = apply_relocations(sechdrs, strtab, symindex, i,
9532600990eSRalf Baechle  							&mod);
954e01402b1SRalf Baechle  			else if (sechdrs[i].sh_type == SHT_RELA)
955e01402b1SRalf Baechle  				err = apply_relocate_add(sechdrs, strtab, symindex, i,
956e01402b1SRalf Baechle  							 &mod);
9572600990eSRalf Baechle  			if (err < 0)
9582600990eSRalf Baechle  				return err;
9592600990eSRalf Baechle 
9602600990eSRalf Baechle   		}
9612600990eSRalf Baechle   	} else {
962bdf5d42cSRalf Baechle 		struct elf_phdr *phdr = (struct elf_phdr *) ((char *)hdr + hdr->e_phoff);
9632600990eSRalf Baechle 
964bdf5d42cSRalf Baechle 		for (i = 0; i < hdr->e_phnum; i++) {
965b618336aSKevin D. Kissell 			if (phdr->p_type == PT_LOAD) {
966b618336aSKevin D. Kissell 				memcpy((void *)phdr->p_paddr,
967b618336aSKevin D. Kissell 				       (char *)hdr + phdr->p_offset,
968b618336aSKevin D. Kissell 				       phdr->p_filesz);
969b618336aSKevin D. Kissell 				memset((void *)phdr->p_paddr + phdr->p_filesz,
970b618336aSKevin D. Kissell 				       0, phdr->p_memsz - phdr->p_filesz);
971b618336aSKevin D. Kissell 		    }
972bdf5d42cSRalf Baechle 		    phdr++;
973bdf5d42cSRalf Baechle 		}
974bdf5d42cSRalf Baechle 
975bdf5d42cSRalf Baechle 		for (i = 0; i < hdr->e_shnum; i++) {
9762600990eSRalf Baechle  			/* Internal symbols and strings. */
9772600990eSRalf Baechle  			if (sechdrs[i].sh_type == SHT_SYMTAB) {
9782600990eSRalf Baechle  				symindex = i;
9792600990eSRalf Baechle  				strindex = sechdrs[i].sh_link;
9802600990eSRalf Baechle  				strtab = (char *)hdr + sechdrs[strindex].sh_offset;
9812600990eSRalf Baechle 
9822600990eSRalf Baechle  				/* mark the symtab's address for when we try to find the
9832600990eSRalf Baechle  				   magic symbols */
9842600990eSRalf Baechle  				sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
9852600990eSRalf Baechle  			}
986e01402b1SRalf Baechle 		}
987e01402b1SRalf Baechle 	}
988e01402b1SRalf Baechle 
989e01402b1SRalf Baechle 	/* make sure it's physically written out */
990e01402b1SRalf Baechle 	flush_icache_range((unsigned long)v->load_addr,
991e01402b1SRalf Baechle 			   (unsigned long)v->load_addr + v->len);
992e01402b1SRalf Baechle 
993e01402b1SRalf Baechle 	if ((find_vpe_symbols(v, sechdrs, symindex, strtab, &mod)) < 0) {
9942600990eSRalf Baechle 		if (v->__start == 0) {
9952600990eSRalf Baechle 			printk(KERN_WARNING "VPE loader: program does not contain "
9962600990eSRalf Baechle 			       "a __start symbol\n");
9972600990eSRalf Baechle 			return -ENOEXEC;
9982600990eSRalf Baechle 		}
999e01402b1SRalf Baechle 
10002600990eSRalf Baechle 		if (v->shared_ptr == NULL)
10012600990eSRalf Baechle 			printk(KERN_WARNING "VPE loader: "
10022600990eSRalf Baechle 			       "program does not contain vpe_shared symbol.\n"
10032600990eSRalf Baechle 			       " Unable to use AMVP (AP/SP) facilities.\n");
1004e01402b1SRalf Baechle 	}
1005e01402b1SRalf Baechle 
1006e01402b1SRalf Baechle 	printk(" elf loaded\n");
10072600990eSRalf Baechle 	return 0;
1008e01402b1SRalf Baechle }
1009e01402b1SRalf Baechle 
10102600990eSRalf Baechle static void cleanup_tc(struct tc *tc)
1011e01402b1SRalf Baechle {
101207cc0c9eSRalf Baechle 	unsigned long flags;
101307cc0c9eSRalf Baechle 	unsigned int mtflags, vpflags;
10142600990eSRalf Baechle 	int tmp;
1015e01402b1SRalf Baechle 
101607cc0c9eSRalf Baechle 	local_irq_save(flags);
101707cc0c9eSRalf Baechle 	mtflags = dmt();
101807cc0c9eSRalf Baechle 	vpflags = dvpe();
10192600990eSRalf Baechle 	/* Put MVPE's into 'configuration state' */
10202600990eSRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1021e01402b1SRalf Baechle 
10222600990eSRalf Baechle 	settc(tc->index);
1023e01402b1SRalf Baechle 	tmp = read_tc_c0_tcstatus();
1024e01402b1SRalf Baechle 
1025e01402b1SRalf Baechle 	/* mark not allocated and not dynamically allocatable */
1026e01402b1SRalf Baechle 	tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1027e01402b1SRalf Baechle 	tmp |= TCSTATUS_IXMT;	/* interrupt exempt */
1028e01402b1SRalf Baechle 	write_tc_c0_tcstatus(tmp);
1029e01402b1SRalf Baechle 
1030e01402b1SRalf Baechle 	write_tc_c0_tchalt(TCHALT_H);
10317c3a622dSNigel Stephens 	mips_ihb();
1032e01402b1SRalf Baechle 
10332600990eSRalf Baechle 	/* bind it to anything other than VPE1 */
103407cc0c9eSRalf Baechle //	write_tc_c0_tcbind(read_tc_c0_tcbind() & ~TCBIND_CURVPE); // | TCBIND_CURVPE
10352600990eSRalf Baechle 
10362600990eSRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
103707cc0c9eSRalf Baechle 	evpe(vpflags);
103807cc0c9eSRalf Baechle 	emt(mtflags);
103907cc0c9eSRalf Baechle 	local_irq_restore(flags);
10402600990eSRalf Baechle }
10412600990eSRalf Baechle 
10422600990eSRalf Baechle static int getcwd(char *buff, int size)
10432600990eSRalf Baechle {
10442600990eSRalf Baechle 	mm_segment_t old_fs;
10452600990eSRalf Baechle 	int ret;
10462600990eSRalf Baechle 
10472600990eSRalf Baechle 	old_fs = get_fs();
10482600990eSRalf Baechle 	set_fs(KERNEL_DS);
10492600990eSRalf Baechle 
10502600990eSRalf Baechle 	ret = sys_getcwd(buff, size);
10512600990eSRalf Baechle 
10522600990eSRalf Baechle 	set_fs(old_fs);
10532600990eSRalf Baechle 
10542600990eSRalf Baechle 	return ret;
10552600990eSRalf Baechle }
10562600990eSRalf Baechle 
10572600990eSRalf Baechle /* checks VPE is unused and gets ready to load program  */
10582600990eSRalf Baechle static int vpe_open(struct inode *inode, struct file *filp)
10592600990eSRalf Baechle {
1060c4c4018bSRalf Baechle 	enum vpe_state state;
10612600990eSRalf Baechle 	struct vpe_notifications *not;
106207cc0c9eSRalf Baechle 	struct vpe *v;
10637558da94SJonathan Corbet 	int ret, err = 0;
10642600990eSRalf Baechle 
10657558da94SJonathan Corbet 	lock_kernel();
106607cc0c9eSRalf Baechle 	if (minor != iminor(inode)) {
106707cc0c9eSRalf Baechle 		/* assume only 1 device at the moment. */
10682600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: only vpe1 is supported\n");
10697558da94SJonathan Corbet 		err = -ENODEV;
10707558da94SJonathan Corbet 		goto out;
10712600990eSRalf Baechle 	}
10722600990eSRalf Baechle 
107307cc0c9eSRalf Baechle 	if ((v = get_vpe(tclimit)) == NULL) {
10742600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: unable to get vpe\n");
10757558da94SJonathan Corbet 		err = -ENODEV;
10767558da94SJonathan Corbet 		goto out;
10772600990eSRalf Baechle 	}
10782600990eSRalf Baechle 
1079c4c4018bSRalf Baechle 	state = xchg(&v->state, VPE_STATE_INUSE);
1080c4c4018bSRalf Baechle 	if (state != VPE_STATE_UNUSED) {
10812600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: tc in use dumping regs\n");
10822600990eSRalf Baechle 
10832600990eSRalf Baechle 		list_for_each_entry(not, &v->notify, list) {
108407cc0c9eSRalf Baechle 			not->stop(tclimit);
10852600990eSRalf Baechle 		}
10862600990eSRalf Baechle 
10872600990eSRalf Baechle 		release_progmem(v->load_addr);
108807cc0c9eSRalf Baechle 		cleanup_tc(get_tc(tclimit));
1089e01402b1SRalf Baechle 	}
1090e01402b1SRalf Baechle 
1091e01402b1SRalf Baechle 	/* this of-course trashes what was there before... */
1092e01402b1SRalf Baechle 	v->pbuffer = vmalloc(P_SIZE);
1093e01402b1SRalf Baechle 	v->plen = P_SIZE;
1094e01402b1SRalf Baechle 	v->load_addr = NULL;
1095e01402b1SRalf Baechle 	v->len = 0;
1096e01402b1SRalf Baechle 
1097d76b0d9bSDavid Howells 	v->uid = filp->f_cred->fsuid;
1098d76b0d9bSDavid Howells 	v->gid = filp->f_cred->fsgid;
10992600990eSRalf Baechle 
11002600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
11012600990eSRalf Baechle 	/* get kspd to tell us when a syscall_exit happens */
11022600990eSRalf Baechle 	if (!kspd_events_reqd) {
11032600990eSRalf Baechle 		kspd_notify(&kspd_events);
11042600990eSRalf Baechle 		kspd_events_reqd++;
11052600990eSRalf Baechle 	}
11062600990eSRalf Baechle #endif
11072600990eSRalf Baechle 
11082600990eSRalf Baechle 	v->cwd[0] = 0;
11092600990eSRalf Baechle 	ret = getcwd(v->cwd, VPE_PATH_MAX);
11102600990eSRalf Baechle 	if (ret < 0)
11112600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: open, getcwd returned %d\n", ret);
11122600990eSRalf Baechle 
11132600990eSRalf Baechle 	v->shared_ptr = NULL;
11142600990eSRalf Baechle 	v->__start = 0;
111507cc0c9eSRalf Baechle 
11167558da94SJonathan Corbet out:
11177558da94SJonathan Corbet 	unlock_kernel();
1118e01402b1SRalf Baechle 	return 0;
1119e01402b1SRalf Baechle }
1120e01402b1SRalf Baechle 
1121e01402b1SRalf Baechle static int vpe_release(struct inode *inode, struct file *filp)
1122e01402b1SRalf Baechle {
1123307bd284SRalf Baechle 	struct vpe *v;
1124e01402b1SRalf Baechle 	Elf_Ehdr *hdr;
112507cc0c9eSRalf Baechle 	int ret = 0;
1126e01402b1SRalf Baechle 
112707cc0c9eSRalf Baechle 	v = get_vpe(tclimit);
112807cc0c9eSRalf Baechle 	if (v == NULL)
1129e01402b1SRalf Baechle 		return -ENODEV;
1130e01402b1SRalf Baechle 
1131e01402b1SRalf Baechle 	hdr = (Elf_Ehdr *) v->pbuffer;
1132d303f4a1SCyrill Gorcunov 	if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) == 0) {
113307cc0c9eSRalf Baechle 		if (vpe_elfload(v) >= 0) {
1134e01402b1SRalf Baechle 			vpe_run(v);
113507cc0c9eSRalf Baechle 		} else {
11362600990eSRalf Baechle  			printk(KERN_WARNING "VPE loader: ELF load failed.\n");
1137e01402b1SRalf Baechle 			ret = -ENOEXEC;
1138e01402b1SRalf Baechle 		}
1139e01402b1SRalf Baechle 	} else {
11402600990eSRalf Baechle  		printk(KERN_WARNING "VPE loader: only elf files are supported\n");
1141e01402b1SRalf Baechle 		ret = -ENOEXEC;
1142e01402b1SRalf Baechle 	}
1143e01402b1SRalf Baechle 
11442600990eSRalf Baechle 	/* It's good to be able to run the SP and if it chokes have a look at
11452600990eSRalf Baechle 	   the /dev/rt?. But if we reset the pointer to the shared struct we
11468ebcfc8bSNick Andrew 	   lose what has happened. So perhaps if garbage is sent to the vpe
11472600990eSRalf Baechle 	   device, use it as a trigger for the reset. Hopefully a nice
11482600990eSRalf Baechle 	   executable will be along shortly. */
11492600990eSRalf Baechle 	if (ret < 0)
11502600990eSRalf Baechle 		v->shared_ptr = NULL;
11512600990eSRalf Baechle 
1152e01402b1SRalf Baechle 	// cleanup any temp buffers
1153e01402b1SRalf Baechle 	if (v->pbuffer)
1154e01402b1SRalf Baechle 		vfree(v->pbuffer);
1155e01402b1SRalf Baechle 	v->plen = 0;
1156e01402b1SRalf Baechle 	return ret;
1157e01402b1SRalf Baechle }
1158e01402b1SRalf Baechle 
1159e01402b1SRalf Baechle static ssize_t vpe_write(struct file *file, const char __user * buffer,
1160e01402b1SRalf Baechle 			 size_t count, loff_t * ppos)
1161e01402b1SRalf Baechle {
1162e01402b1SRalf Baechle 	size_t ret = count;
1163307bd284SRalf Baechle 	struct vpe *v;
1164e01402b1SRalf Baechle 
116507cc0c9eSRalf Baechle 	if (iminor(file->f_path.dentry->d_inode) != minor)
116607cc0c9eSRalf Baechle 		return -ENODEV;
116707cc0c9eSRalf Baechle 
116807cc0c9eSRalf Baechle 	v = get_vpe(tclimit);
116907cc0c9eSRalf Baechle 	if (v == NULL)
1170e01402b1SRalf Baechle 		return -ENODEV;
1171e01402b1SRalf Baechle 
1172e01402b1SRalf Baechle 	if (v->pbuffer == NULL) {
11732600990eSRalf Baechle 		printk(KERN_ERR "VPE loader: no buffer for program\n");
1174e01402b1SRalf Baechle 		return -ENOMEM;
1175e01402b1SRalf Baechle 	}
1176e01402b1SRalf Baechle 
1177e01402b1SRalf Baechle 	if ((count + v->len) > v->plen) {
1178e01402b1SRalf Baechle 		printk(KERN_WARNING
11792600990eSRalf Baechle 		       "VPE loader: elf size too big. Perhaps strip uneeded symbols\n");
1180e01402b1SRalf Baechle 		return -ENOMEM;
1181e01402b1SRalf Baechle 	}
1182e01402b1SRalf Baechle 
1183e01402b1SRalf Baechle 	count -= copy_from_user(v->pbuffer + v->len, buffer, count);
11842600990eSRalf Baechle 	if (!count)
1185e01402b1SRalf Baechle 		return -EFAULT;
1186e01402b1SRalf Baechle 
1187e01402b1SRalf Baechle 	v->len += count;
1188e01402b1SRalf Baechle 	return ret;
1189e01402b1SRalf Baechle }
1190e01402b1SRalf Baechle 
11915dfe4c96SArjan van de Ven static const struct file_operations vpe_fops = {
1192e01402b1SRalf Baechle 	.owner = THIS_MODULE,
1193e01402b1SRalf Baechle 	.open = vpe_open,
1194e01402b1SRalf Baechle 	.release = vpe_release,
1195e01402b1SRalf Baechle 	.write = vpe_write
1196e01402b1SRalf Baechle };
1197e01402b1SRalf Baechle 
1198e01402b1SRalf Baechle /* module wrapper entry points */
1199e01402b1SRalf Baechle /* give me a vpe */
1200e01402b1SRalf Baechle vpe_handle vpe_alloc(void)
1201e01402b1SRalf Baechle {
1202e01402b1SRalf Baechle 	int i;
1203e01402b1SRalf Baechle 	struct vpe *v;
1204e01402b1SRalf Baechle 
1205e01402b1SRalf Baechle 	/* find a vpe */
1206e01402b1SRalf Baechle 	for (i = 1; i < MAX_VPES; i++) {
1207e01402b1SRalf Baechle 		if ((v = get_vpe(i)) != NULL) {
1208e01402b1SRalf Baechle 			v->state = VPE_STATE_INUSE;
1209e01402b1SRalf Baechle 			return v;
1210e01402b1SRalf Baechle 		}
1211e01402b1SRalf Baechle 	}
1212e01402b1SRalf Baechle 	return NULL;
1213e01402b1SRalf Baechle }
1214e01402b1SRalf Baechle 
1215e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_alloc);
1216e01402b1SRalf Baechle 
1217e01402b1SRalf Baechle /* start running from here */
1218e01402b1SRalf Baechle int vpe_start(vpe_handle vpe, unsigned long start)
1219e01402b1SRalf Baechle {
1220e01402b1SRalf Baechle 	struct vpe *v = vpe;
1221e01402b1SRalf Baechle 
1222e01402b1SRalf Baechle 	v->__start = start;
1223e01402b1SRalf Baechle 	return vpe_run(v);
1224e01402b1SRalf Baechle }
1225e01402b1SRalf Baechle 
1226e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_start);
1227e01402b1SRalf Baechle 
1228e01402b1SRalf Baechle /* halt it for now */
1229e01402b1SRalf Baechle int vpe_stop(vpe_handle vpe)
1230e01402b1SRalf Baechle {
1231e01402b1SRalf Baechle 	struct vpe *v = vpe;
1232e01402b1SRalf Baechle 	struct tc *t;
1233e01402b1SRalf Baechle 	unsigned int evpe_flags;
1234e01402b1SRalf Baechle 
1235e01402b1SRalf Baechle 	evpe_flags = dvpe();
1236e01402b1SRalf Baechle 
1237e01402b1SRalf Baechle 	if ((t = list_entry(v->tc.next, struct tc, tc)) != NULL) {
1238e01402b1SRalf Baechle 
1239e01402b1SRalf Baechle 		settc(t->index);
1240e01402b1SRalf Baechle 		write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
1241e01402b1SRalf Baechle 	}
1242e01402b1SRalf Baechle 
1243e01402b1SRalf Baechle 	evpe(evpe_flags);
1244e01402b1SRalf Baechle 
1245e01402b1SRalf Baechle 	return 0;
1246e01402b1SRalf Baechle }
1247e01402b1SRalf Baechle 
1248e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_stop);
1249e01402b1SRalf Baechle 
1250e01402b1SRalf Baechle /* I've done with it thank you */
1251e01402b1SRalf Baechle int vpe_free(vpe_handle vpe)
1252e01402b1SRalf Baechle {
1253e01402b1SRalf Baechle 	struct vpe *v = vpe;
1254e01402b1SRalf Baechle 	struct tc *t;
1255e01402b1SRalf Baechle 	unsigned int evpe_flags;
1256e01402b1SRalf Baechle 
1257e01402b1SRalf Baechle 	if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
1258e01402b1SRalf Baechle 		return -ENOEXEC;
1259e01402b1SRalf Baechle 	}
1260e01402b1SRalf Baechle 
1261e01402b1SRalf Baechle 	evpe_flags = dvpe();
1262e01402b1SRalf Baechle 
1263e01402b1SRalf Baechle 	/* Put MVPE's into 'configuration state' */
1264340ee4b9SRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1265e01402b1SRalf Baechle 
1266e01402b1SRalf Baechle 	settc(t->index);
1267e01402b1SRalf Baechle 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
1268e01402b1SRalf Baechle 
12697c3a622dSNigel Stephens 	/* halt the TC */
1270e01402b1SRalf Baechle 	write_tc_c0_tchalt(TCHALT_H);
12717c3a622dSNigel Stephens 	mips_ihb();
12727c3a622dSNigel Stephens 
12737c3a622dSNigel Stephens 	/* mark the TC unallocated */
12747c3a622dSNigel Stephens 	write_tc_c0_tcstatus(read_tc_c0_tcstatus() & ~TCSTATUS_A);
1275e01402b1SRalf Baechle 
1276e01402b1SRalf Baechle 	v->state = VPE_STATE_UNUSED;
1277e01402b1SRalf Baechle 
1278340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
1279e01402b1SRalf Baechle 	evpe(evpe_flags);
1280e01402b1SRalf Baechle 
1281e01402b1SRalf Baechle 	return 0;
1282e01402b1SRalf Baechle }
1283e01402b1SRalf Baechle 
1284e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_free);
1285e01402b1SRalf Baechle 
1286e01402b1SRalf Baechle void *vpe_get_shared(int index)
1287e01402b1SRalf Baechle {
1288e01402b1SRalf Baechle 	struct vpe *v;
1289e01402b1SRalf Baechle 
12902600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
1291e01402b1SRalf Baechle 		return NULL;
1292e01402b1SRalf Baechle 
1293e01402b1SRalf Baechle 	return v->shared_ptr;
1294e01402b1SRalf Baechle }
1295e01402b1SRalf Baechle 
1296e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_get_shared);
1297e01402b1SRalf Baechle 
12982600990eSRalf Baechle int vpe_getuid(int index)
12992600990eSRalf Baechle {
13002600990eSRalf Baechle 	struct vpe *v;
13012600990eSRalf Baechle 
13022600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13032600990eSRalf Baechle 		return -1;
13042600990eSRalf Baechle 
13052600990eSRalf Baechle 	return v->uid;
13062600990eSRalf Baechle }
13072600990eSRalf Baechle 
13082600990eSRalf Baechle EXPORT_SYMBOL(vpe_getuid);
13092600990eSRalf Baechle 
13102600990eSRalf Baechle int vpe_getgid(int index)
13112600990eSRalf Baechle {
13122600990eSRalf Baechle 	struct vpe *v;
13132600990eSRalf Baechle 
13142600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13152600990eSRalf Baechle 		return -1;
13162600990eSRalf Baechle 
13172600990eSRalf Baechle 	return v->gid;
13182600990eSRalf Baechle }
13192600990eSRalf Baechle 
13202600990eSRalf Baechle EXPORT_SYMBOL(vpe_getgid);
13212600990eSRalf Baechle 
13222600990eSRalf Baechle int vpe_notify(int index, struct vpe_notifications *notify)
13232600990eSRalf Baechle {
13242600990eSRalf Baechle 	struct vpe *v;
13252600990eSRalf Baechle 
13262600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13272600990eSRalf Baechle 		return -1;
13282600990eSRalf Baechle 
13292600990eSRalf Baechle 	list_add(&notify->list, &v->notify);
13302600990eSRalf Baechle 	return 0;
13312600990eSRalf Baechle }
13322600990eSRalf Baechle 
13332600990eSRalf Baechle EXPORT_SYMBOL(vpe_notify);
13342600990eSRalf Baechle 
13352600990eSRalf Baechle char *vpe_getcwd(int index)
13362600990eSRalf Baechle {
13372600990eSRalf Baechle 	struct vpe *v;
13382600990eSRalf Baechle 
13392600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13402600990eSRalf Baechle 		return NULL;
13412600990eSRalf Baechle 
13422600990eSRalf Baechle 	return v->cwd;
13432600990eSRalf Baechle }
13442600990eSRalf Baechle 
13452600990eSRalf Baechle EXPORT_SYMBOL(vpe_getcwd);
13462600990eSRalf Baechle 
13472600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
13482600990eSRalf Baechle static void kspd_sp_exit( int sp_id)
13492600990eSRalf Baechle {
13502600990eSRalf Baechle 	cleanup_tc(get_tc(sp_id));
13512600990eSRalf Baechle }
13522600990eSRalf Baechle #endif
13532600990eSRalf Baechle 
1354736fad17SKay Sievers static ssize_t store_kill(struct device *dev, struct device_attribute *attr,
1355736fad17SKay Sievers 			  const char *buf, size_t len)
13560f5d0df3SRalf Baechle {
13570f5d0df3SRalf Baechle 	struct vpe *vpe = get_vpe(tclimit);
13580f5d0df3SRalf Baechle 	struct vpe_notifications *not;
13590f5d0df3SRalf Baechle 
13600f5d0df3SRalf Baechle 	list_for_each_entry(not, &vpe->notify, list) {
13610f5d0df3SRalf Baechle 		not->stop(tclimit);
13620f5d0df3SRalf Baechle 	}
13630f5d0df3SRalf Baechle 
13640f5d0df3SRalf Baechle 	release_progmem(vpe->load_addr);
13650f5d0df3SRalf Baechle 	cleanup_tc(get_tc(tclimit));
13660f5d0df3SRalf Baechle 	vpe_stop(vpe);
13670f5d0df3SRalf Baechle 	vpe_free(vpe);
13680f5d0df3SRalf Baechle 
13690f5d0df3SRalf Baechle 	return len;
13700f5d0df3SRalf Baechle }
13710f5d0df3SRalf Baechle 
1372736fad17SKay Sievers static ssize_t show_ntcs(struct device *cd, struct device_attribute *attr,
1373736fad17SKay Sievers 			 char *buf)
137441790e04SRalf Baechle {
137541790e04SRalf Baechle 	struct vpe *vpe = get_vpe(tclimit);
137641790e04SRalf Baechle 
137741790e04SRalf Baechle 	return sprintf(buf, "%d\n", vpe->ntcs);
137841790e04SRalf Baechle }
137941790e04SRalf Baechle 
1380736fad17SKay Sievers static ssize_t store_ntcs(struct device *dev, struct device_attribute *attr,
1381736fad17SKay Sievers 			  const char *buf, size_t len)
138241790e04SRalf Baechle {
138341790e04SRalf Baechle 	struct vpe *vpe = get_vpe(tclimit);
138441790e04SRalf Baechle 	unsigned long new;
138541790e04SRalf Baechle 	char *endp;
138641790e04SRalf Baechle 
138741790e04SRalf Baechle 	new = simple_strtoul(buf, &endp, 0);
138841790e04SRalf Baechle 	if (endp == buf)
138941790e04SRalf Baechle 		goto out_einval;
139041790e04SRalf Baechle 
139141790e04SRalf Baechle 	if (new == 0 || new > (hw_tcs - tclimit))
139241790e04SRalf Baechle 		goto out_einval;
139341790e04SRalf Baechle 
139441790e04SRalf Baechle 	vpe->ntcs = new;
139541790e04SRalf Baechle 
139641790e04SRalf Baechle 	return len;
139741790e04SRalf Baechle 
139841790e04SRalf Baechle out_einval:
139952a7a27cSJoe Perches 	return -EINVAL;
140041790e04SRalf Baechle }
140141790e04SRalf Baechle 
1402736fad17SKay Sievers static struct device_attribute vpe_class_attributes[] = {
14030f5d0df3SRalf Baechle 	__ATTR(kill, S_IWUSR, NULL, store_kill),
140441790e04SRalf Baechle 	__ATTR(ntcs, S_IRUGO | S_IWUSR, show_ntcs, store_ntcs),
140541790e04SRalf Baechle 	{}
140641790e04SRalf Baechle };
140741790e04SRalf Baechle 
1408736fad17SKay Sievers static void vpe_device_release(struct device *cd)
140941790e04SRalf Baechle {
141041790e04SRalf Baechle 	kfree(cd);
141141790e04SRalf Baechle }
141241790e04SRalf Baechle 
141341790e04SRalf Baechle struct class vpe_class = {
141441790e04SRalf Baechle 	.name = "vpe",
141541790e04SRalf Baechle 	.owner = THIS_MODULE,
1416736fad17SKay Sievers 	.dev_release = vpe_device_release,
1417736fad17SKay Sievers 	.dev_attrs = vpe_class_attributes,
141841790e04SRalf Baechle };
141941790e04SRalf Baechle 
1420736fad17SKay Sievers struct device vpe_device;
142127a3bbafSRalf Baechle 
1422e01402b1SRalf Baechle static int __init vpe_module_init(void)
1423e01402b1SRalf Baechle {
142407cc0c9eSRalf Baechle 	unsigned int mtflags, vpflags;
142507cc0c9eSRalf Baechle 	unsigned long flags, val;
1426e01402b1SRalf Baechle 	struct vpe *v = NULL;
1427e01402b1SRalf Baechle 	struct tc *t;
142841790e04SRalf Baechle 	int tc, err;
1429e01402b1SRalf Baechle 
1430e01402b1SRalf Baechle 	if (!cpu_has_mipsmt) {
1431e01402b1SRalf Baechle 		printk("VPE loader: not a MIPS MT capable processor\n");
1432e01402b1SRalf Baechle 		return -ENODEV;
1433e01402b1SRalf Baechle 	}
1434e01402b1SRalf Baechle 
143507cc0c9eSRalf Baechle 	if (vpelimit == 0) {
143607cc0c9eSRalf Baechle 		printk(KERN_WARNING "No VPEs reserved for AP/SP, not "
143707cc0c9eSRalf Baechle 		       "initializing VPE loader.\nPass maxvpes=<n> argument as "
143807cc0c9eSRalf Baechle 		       "kernel argument\n");
143907cc0c9eSRalf Baechle 
144007cc0c9eSRalf Baechle 		return -ENODEV;
144107cc0c9eSRalf Baechle 	}
144207cc0c9eSRalf Baechle 
144307cc0c9eSRalf Baechle 	if (tclimit == 0) {
144407cc0c9eSRalf Baechle 		printk(KERN_WARNING "No TCs reserved for AP/SP, not "
144507cc0c9eSRalf Baechle 		       "initializing VPE loader.\nPass maxtcs=<n> argument as "
144607cc0c9eSRalf Baechle 		       "kernel argument\n");
144707cc0c9eSRalf Baechle 
144807cc0c9eSRalf Baechle 		return -ENODEV;
144907cc0c9eSRalf Baechle 	}
145007cc0c9eSRalf Baechle 
1451682e852eSAlexey Dobriyan 	major = register_chrdev(0, module_name, &vpe_fops);
1452682e852eSAlexey Dobriyan 	if (major < 0) {
1453e01402b1SRalf Baechle 		printk("VPE loader: unable to register character device\n");
1454307bd284SRalf Baechle 		return major;
1455e01402b1SRalf Baechle 	}
1456e01402b1SRalf Baechle 
145741790e04SRalf Baechle 	err = class_register(&vpe_class);
145841790e04SRalf Baechle 	if (err) {
145941790e04SRalf Baechle 		printk(KERN_ERR "vpe_class registration failed\n");
146027a3bbafSRalf Baechle 		goto out_chrdev;
146127a3bbafSRalf Baechle 	}
146241790e04SRalf Baechle 
1463736fad17SKay Sievers 	device_initialize(&vpe_device);
146441790e04SRalf Baechle 	vpe_device.class	= &vpe_class,
146541790e04SRalf Baechle 	vpe_device.parent	= NULL,
14661bb5beb4SKay Sievers 	dev_set_name(&vpe_device, "vpe1");
146741790e04SRalf Baechle 	vpe_device.devt = MKDEV(major, minor);
1468736fad17SKay Sievers 	err = device_add(&vpe_device);
146941790e04SRalf Baechle 	if (err) {
147041790e04SRalf Baechle 		printk(KERN_ERR "Adding vpe_device failed\n");
147141790e04SRalf Baechle 		goto out_class;
147241790e04SRalf Baechle 	}
147327a3bbafSRalf Baechle 
147407cc0c9eSRalf Baechle 	local_irq_save(flags);
147507cc0c9eSRalf Baechle 	mtflags = dmt();
147607cc0c9eSRalf Baechle 	vpflags = dvpe();
1477e01402b1SRalf Baechle 
1478e01402b1SRalf Baechle 	/* Put MVPE's into 'configuration state' */
1479340ee4b9SRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1480e01402b1SRalf Baechle 
1481e01402b1SRalf Baechle 	/* dump_mtregs(); */
1482e01402b1SRalf Baechle 
1483e01402b1SRalf Baechle 	val = read_c0_mvpconf0();
148407cc0c9eSRalf Baechle 	hw_tcs = (val & MVPCONF0_PTC) + 1;
148507cc0c9eSRalf Baechle 	hw_vpes = ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1;
148607cc0c9eSRalf Baechle 
148707cc0c9eSRalf Baechle 	for (tc = tclimit; tc < hw_tcs; tc++) {
148807cc0c9eSRalf Baechle 		/*
148907cc0c9eSRalf Baechle 		 * Must re-enable multithreading temporarily or in case we
149007cc0c9eSRalf Baechle 		 * reschedule send IPIs or similar we might hang.
149107cc0c9eSRalf Baechle 		 */
149207cc0c9eSRalf Baechle 		clear_c0_mvpcontrol(MVPCONTROL_VPC);
149307cc0c9eSRalf Baechle 		evpe(vpflags);
149407cc0c9eSRalf Baechle 		emt(mtflags);
149507cc0c9eSRalf Baechle 		local_irq_restore(flags);
149607cc0c9eSRalf Baechle 		t = alloc_tc(tc);
149707cc0c9eSRalf Baechle 		if (!t) {
149807cc0c9eSRalf Baechle 			err = -ENOMEM;
149907cc0c9eSRalf Baechle 			goto out;
150007cc0c9eSRalf Baechle 		}
150107cc0c9eSRalf Baechle 
150207cc0c9eSRalf Baechle 		local_irq_save(flags);
150307cc0c9eSRalf Baechle 		mtflags = dmt();
150407cc0c9eSRalf Baechle 		vpflags = dvpe();
150507cc0c9eSRalf Baechle 		set_c0_mvpcontrol(MVPCONTROL_VPC);
1506e01402b1SRalf Baechle 
1507e01402b1SRalf Baechle 		/* VPE's */
150807cc0c9eSRalf Baechle 		if (tc < hw_tcs) {
150907cc0c9eSRalf Baechle 			settc(tc);
1510e01402b1SRalf Baechle 
151107cc0c9eSRalf Baechle 			if ((v = alloc_vpe(tc)) == NULL) {
1512e01402b1SRalf Baechle 				printk(KERN_WARNING "VPE: unable to allocate VPE\n");
151307cc0c9eSRalf Baechle 
151407cc0c9eSRalf Baechle 				goto out_reenable;
1515e01402b1SRalf Baechle 			}
1516e01402b1SRalf Baechle 
151741790e04SRalf Baechle 			v->ntcs = hw_tcs - tclimit;
151841790e04SRalf Baechle 
15192600990eSRalf Baechle 			/* add the tc to the list of this vpe's tc's. */
15202600990eSRalf Baechle 			list_add(&t->tc, &v->tc);
1521e01402b1SRalf Baechle 
1522e01402b1SRalf Baechle 			/* deactivate all but vpe0 */
152307cc0c9eSRalf Baechle 			if (tc >= tclimit) {
1524e01402b1SRalf Baechle 				unsigned long tmp = read_vpe_c0_vpeconf0();
1525e01402b1SRalf Baechle 
1526e01402b1SRalf Baechle 				tmp &= ~VPECONF0_VPA;
1527e01402b1SRalf Baechle 
1528e01402b1SRalf Baechle 				/* master VPE */
1529e01402b1SRalf Baechle 				tmp |= VPECONF0_MVP;
1530e01402b1SRalf Baechle 				write_vpe_c0_vpeconf0(tmp);
1531e01402b1SRalf Baechle 			}
1532e01402b1SRalf Baechle 
1533e01402b1SRalf Baechle 			/* disable multi-threading with TC's */
1534e01402b1SRalf Baechle 			write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE);
1535e01402b1SRalf Baechle 
153607cc0c9eSRalf Baechle 			if (tc >= vpelimit) {
15372600990eSRalf Baechle 				/*
15382600990eSRalf Baechle 				 * Set config to be the same as vpe0,
15392600990eSRalf Baechle 				 * particularly kseg0 coherency alg
15402600990eSRalf Baechle 				 */
1541e01402b1SRalf Baechle 				write_vpe_c0_config(read_c0_config());
1542e01402b1SRalf Baechle 			}
1543e01402b1SRalf Baechle 		}
1544e01402b1SRalf Baechle 
1545e01402b1SRalf Baechle 		/* TC's */
1546e01402b1SRalf Baechle 		t->pvpe = v;	/* set the parent vpe */
1547e01402b1SRalf Baechle 
154807cc0c9eSRalf Baechle 		if (tc >= tclimit) {
1549e01402b1SRalf Baechle 			unsigned long tmp;
1550e01402b1SRalf Baechle 
155107cc0c9eSRalf Baechle 			settc(tc);
1552e01402b1SRalf Baechle 
15532600990eSRalf Baechle 			/* Any TC that is bound to VPE0 gets left as is - in case
15542600990eSRalf Baechle 			   we are running SMTC on VPE0. A TC that is bound to any
15552600990eSRalf Baechle 			   other VPE gets bound to VPE0, ideally I'd like to make
15562600990eSRalf Baechle 			   it homeless but it doesn't appear to let me bind a TC
15572600990eSRalf Baechle 			   to a non-existent VPE. Which is perfectly reasonable.
15582600990eSRalf Baechle 
15592600990eSRalf Baechle 			   The (un)bound state is visible to an EJTAG probe so may
15602600990eSRalf Baechle 			   notify GDB...
15612600990eSRalf Baechle 			*/
15622600990eSRalf Baechle 
15632600990eSRalf Baechle 			if (((tmp = read_tc_c0_tcbind()) & TCBIND_CURVPE)) {
15642600990eSRalf Baechle 				/* tc is bound >vpe0 */
15652600990eSRalf Baechle 				write_tc_c0_tcbind(tmp & ~TCBIND_CURVPE);
15662600990eSRalf Baechle 
15672600990eSRalf Baechle 				t->pvpe = get_vpe(0);	/* set the parent vpe */
15682600990eSRalf Baechle 			}
1569e01402b1SRalf Baechle 
15707c3a622dSNigel Stephens 			/* halt the TC */
15717c3a622dSNigel Stephens 			write_tc_c0_tchalt(TCHALT_H);
15727c3a622dSNigel Stephens 			mips_ihb();
15737c3a622dSNigel Stephens 
1574e01402b1SRalf Baechle 			tmp = read_tc_c0_tcstatus();
1575e01402b1SRalf Baechle 
15762600990eSRalf Baechle 			/* mark not activated and not dynamically allocatable */
1577e01402b1SRalf Baechle 			tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1578e01402b1SRalf Baechle 			tmp |= TCSTATUS_IXMT;	/* interrupt exempt */
1579e01402b1SRalf Baechle 			write_tc_c0_tcstatus(tmp);
1580e01402b1SRalf Baechle 		}
1581e01402b1SRalf Baechle 	}
1582e01402b1SRalf Baechle 
158307cc0c9eSRalf Baechle out_reenable:
1584e01402b1SRalf Baechle 	/* release config state */
1585340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
1586e01402b1SRalf Baechle 
158707cc0c9eSRalf Baechle 	evpe(vpflags);
158807cc0c9eSRalf Baechle 	emt(mtflags);
158907cc0c9eSRalf Baechle 	local_irq_restore(flags);
159007cc0c9eSRalf Baechle 
15912600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
15922600990eSRalf Baechle 	kspd_events.kspd_sp_exit = kspd_sp_exit;
15932600990eSRalf Baechle #endif
1594e01402b1SRalf Baechle 	return 0;
159527a3bbafSRalf Baechle 
159641790e04SRalf Baechle out_class:
159741790e04SRalf Baechle 	class_unregister(&vpe_class);
159827a3bbafSRalf Baechle out_chrdev:
159927a3bbafSRalf Baechle 	unregister_chrdev(major, module_name);
160027a3bbafSRalf Baechle 
160107cc0c9eSRalf Baechle out:
160227a3bbafSRalf Baechle 	return err;
1603e01402b1SRalf Baechle }
1604e01402b1SRalf Baechle 
1605e01402b1SRalf Baechle static void __exit vpe_module_exit(void)
1606e01402b1SRalf Baechle {
1607e01402b1SRalf Baechle 	struct vpe *v, *n;
1608e01402b1SRalf Baechle 
1609e01402b1SRalf Baechle 	list_for_each_entry_safe(v, n, &vpecontrol.vpe_list, list) {
1610e01402b1SRalf Baechle 		if (v->state != VPE_STATE_UNUSED) {
1611e01402b1SRalf Baechle 			release_vpe(v);
1612e01402b1SRalf Baechle 		}
1613e01402b1SRalf Baechle 	}
1614e01402b1SRalf Baechle 
1615736fad17SKay Sievers 	device_del(&vpe_device);
1616e01402b1SRalf Baechle 	unregister_chrdev(major, module_name);
1617e01402b1SRalf Baechle }
1618e01402b1SRalf Baechle 
1619e01402b1SRalf Baechle module_init(vpe_module_init);
1620e01402b1SRalf Baechle module_exit(vpe_module_exit);
1621e01402b1SRalf Baechle MODULE_DESCRIPTION("MIPS VPE Loader");
16222600990eSRalf Baechle MODULE_AUTHOR("Elizabeth Oldham, MIPS Technologies, Inc.");
1623e01402b1SRalf Baechle MODULE_LICENSE("GPL");
1624