xref: /openbmc/linux/arch/mips/kernel/vpe.c (revision e2a9cf96)
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 extern void save_gp_address(unsigned int secbase, unsigned int rel);
159e01402b1SRalf Baechle 
160e01402b1SRalf Baechle /* get the vpe associated with this minor */
161e01402b1SRalf Baechle struct vpe *get_vpe(int minor)
162e01402b1SRalf Baechle {
163e01402b1SRalf Baechle 	struct vpe *v;
164e01402b1SRalf Baechle 
1652600990eSRalf Baechle 	if (!cpu_has_mipsmt)
1662600990eSRalf Baechle 		return NULL;
1672600990eSRalf Baechle 
168e01402b1SRalf Baechle 	list_for_each_entry(v, &vpecontrol.vpe_list, list) {
169e01402b1SRalf Baechle 		if (v->minor == minor)
170e01402b1SRalf Baechle 			return v;
171e01402b1SRalf Baechle 	}
172e01402b1SRalf Baechle 
173e01402b1SRalf Baechle 	return NULL;
174e01402b1SRalf Baechle }
175e01402b1SRalf Baechle 
176e01402b1SRalf Baechle /* get the vpe associated with this minor */
177e01402b1SRalf Baechle struct tc *get_tc(int index)
178e01402b1SRalf Baechle {
179e01402b1SRalf Baechle 	struct tc *t;
180e01402b1SRalf Baechle 
181e01402b1SRalf Baechle 	list_for_each_entry(t, &vpecontrol.tc_list, list) {
182e01402b1SRalf Baechle 		if (t->index == index)
183e01402b1SRalf Baechle 			return t;
184e01402b1SRalf Baechle 	}
185e01402b1SRalf Baechle 
186e01402b1SRalf Baechle 	return NULL;
187e01402b1SRalf Baechle }
188e01402b1SRalf Baechle 
189e01402b1SRalf Baechle struct tc *get_tc_unused(void)
190e01402b1SRalf Baechle {
191e01402b1SRalf Baechle 	struct tc *t;
192e01402b1SRalf Baechle 
193e01402b1SRalf Baechle 	list_for_each_entry(t, &vpecontrol.tc_list, list) {
194e01402b1SRalf Baechle 		if (t->state == TC_STATE_UNUSED)
195e01402b1SRalf Baechle 			return t;
196e01402b1SRalf Baechle 	}
197e01402b1SRalf Baechle 
198e01402b1SRalf Baechle 	return NULL;
199e01402b1SRalf Baechle }
200e01402b1SRalf Baechle 
201e01402b1SRalf Baechle /* allocate a vpe and associate it with this minor (or index) */
202e01402b1SRalf Baechle struct vpe *alloc_vpe(int minor)
203e01402b1SRalf Baechle {
204e01402b1SRalf Baechle 	struct vpe *v;
205e01402b1SRalf Baechle 
206307bd284SRalf Baechle 	if ((v = kzalloc(sizeof(struct vpe), GFP_KERNEL)) == NULL) {
207e01402b1SRalf Baechle 		return NULL;
208e01402b1SRalf Baechle 	}
209e01402b1SRalf Baechle 
210e01402b1SRalf Baechle 	INIT_LIST_HEAD(&v->tc);
211e01402b1SRalf Baechle 	list_add_tail(&v->list, &vpecontrol.vpe_list);
212e01402b1SRalf Baechle 
2132600990eSRalf Baechle 	INIT_LIST_HEAD(&v->notify);
214e01402b1SRalf Baechle 	v->minor = minor;
215e01402b1SRalf Baechle 	return v;
216e01402b1SRalf Baechle }
217e01402b1SRalf Baechle 
218e01402b1SRalf Baechle /* allocate a tc. At startup only tc0 is running, all other can be halted. */
219e01402b1SRalf Baechle struct tc *alloc_tc(int index)
220e01402b1SRalf Baechle {
22107cc0c9eSRalf Baechle 	struct tc *tc;
222e01402b1SRalf Baechle 
22307cc0c9eSRalf Baechle 	if ((tc = kzalloc(sizeof(struct tc), GFP_KERNEL)) == NULL)
22407cc0c9eSRalf Baechle 		goto out;
225e01402b1SRalf Baechle 
22607cc0c9eSRalf Baechle 	INIT_LIST_HEAD(&tc->tc);
22707cc0c9eSRalf Baechle 	tc->index = index;
22807cc0c9eSRalf Baechle 	list_add_tail(&tc->list, &vpecontrol.tc_list);
229e01402b1SRalf Baechle 
23007cc0c9eSRalf Baechle out:
23107cc0c9eSRalf Baechle 	return tc;
232e01402b1SRalf Baechle }
233e01402b1SRalf Baechle 
234e01402b1SRalf Baechle /* clean up and free everything */
235e01402b1SRalf Baechle void release_vpe(struct vpe *v)
236e01402b1SRalf Baechle {
237e01402b1SRalf Baechle 	list_del(&v->list);
238e01402b1SRalf Baechle 	if (v->load_addr)
239e01402b1SRalf Baechle 		release_progmem(v);
240e01402b1SRalf Baechle 	kfree(v);
241e01402b1SRalf Baechle }
242e01402b1SRalf Baechle 
243e01402b1SRalf Baechle void dump_mtregs(void)
244e01402b1SRalf Baechle {
245e01402b1SRalf Baechle 	unsigned long val;
246e01402b1SRalf Baechle 
247e01402b1SRalf Baechle 	val = read_c0_config3();
248e01402b1SRalf Baechle 	printk("config3 0x%lx MT %ld\n", val,
249e01402b1SRalf Baechle 	       (val & CONFIG3_MT) >> CONFIG3_MT_SHIFT);
250e01402b1SRalf Baechle 
251e01402b1SRalf Baechle 	val = read_c0_mvpcontrol();
252e01402b1SRalf Baechle 	printk("MVPControl 0x%lx, STLB %ld VPC %ld EVP %ld\n", val,
253e01402b1SRalf Baechle 	       (val & MVPCONTROL_STLB) >> MVPCONTROL_STLB_SHIFT,
254e01402b1SRalf Baechle 	       (val & MVPCONTROL_VPC) >> MVPCONTROL_VPC_SHIFT,
255e01402b1SRalf Baechle 	       (val & MVPCONTROL_EVP));
256e01402b1SRalf Baechle 
2572600990eSRalf Baechle 	val = read_c0_mvpconf0();
2582600990eSRalf Baechle 	printk("mvpconf0 0x%lx, PVPE %ld PTC %ld M %ld\n", val,
2592600990eSRalf Baechle 	       (val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT,
2602600990eSRalf Baechle 	       val & MVPCONF0_PTC, (val & MVPCONF0_M) >> MVPCONF0_M_SHIFT);
261e01402b1SRalf Baechle }
262e01402b1SRalf Baechle 
263e01402b1SRalf Baechle /* Find some VPE program space  */
264571e0bedSRalf Baechle static void *alloc_progmem(unsigned long len)
265e01402b1SRalf Baechle {
2665408c490SRalf Baechle 	void *addr;
2675408c490SRalf Baechle 
268e01402b1SRalf Baechle #ifdef CONFIG_MIPS_VPE_LOADER_TOM
2695408c490SRalf Baechle 	/*
2705408c490SRalf Baechle 	 * This means you must tell Linux to use less memory than you
2715408c490SRalf Baechle 	 * physically have, for example by passing a mem= boot argument.
2725408c490SRalf Baechle 	 */
2739f2546adSRalf Baechle 	addr = pfn_to_kaddr(max_low_pfn);
2745408c490SRalf Baechle 	memset(addr, 0, len);
275e01402b1SRalf Baechle #else
2765408c490SRalf Baechle 	/* simple grab some mem for now */
2775408c490SRalf Baechle 	addr = kzalloc(len, GFP_KERNEL);
278e01402b1SRalf Baechle #endif
2795408c490SRalf Baechle 
2805408c490SRalf Baechle 	return addr;
281e01402b1SRalf Baechle }
282e01402b1SRalf Baechle 
283e01402b1SRalf Baechle static void release_progmem(void *ptr)
284e01402b1SRalf Baechle {
285e01402b1SRalf Baechle #ifndef CONFIG_MIPS_VPE_LOADER_TOM
286e01402b1SRalf Baechle 	kfree(ptr);
287e01402b1SRalf Baechle #endif
288e01402b1SRalf Baechle }
289e01402b1SRalf Baechle 
290e01402b1SRalf Baechle /* Update size with this section: return offset. */
291e01402b1SRalf Baechle static long get_offset(unsigned long *size, Elf_Shdr * sechdr)
292e01402b1SRalf Baechle {
293e01402b1SRalf Baechle 	long ret;
294e01402b1SRalf Baechle 
295e01402b1SRalf Baechle 	ret = ALIGN(*size, sechdr->sh_addralign ? : 1);
296e01402b1SRalf Baechle 	*size = ret + sechdr->sh_size;
297e01402b1SRalf Baechle 	return ret;
298e01402b1SRalf Baechle }
299e01402b1SRalf Baechle 
300e01402b1SRalf Baechle /* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld
301e01402b1SRalf Baechle    might -- code, read-only data, read-write data, small data.  Tally
302e01402b1SRalf Baechle    sizes, and place the offsets into sh_entsize fields: high bit means it
303e01402b1SRalf Baechle    belongs in init. */
304e01402b1SRalf Baechle static void layout_sections(struct module *mod, const Elf_Ehdr * hdr,
305e01402b1SRalf Baechle 			    Elf_Shdr * sechdrs, const char *secstrings)
306e01402b1SRalf Baechle {
307e01402b1SRalf Baechle 	static unsigned long const masks[][2] = {
308e01402b1SRalf Baechle 		/* NOTE: all executable code must be the first section
309e01402b1SRalf Baechle 		 * in this array; otherwise modify the text_size
310e01402b1SRalf Baechle 		 * finder in the two loops below */
311e01402b1SRalf Baechle 		{SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL},
312e01402b1SRalf Baechle 		{SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL},
313e01402b1SRalf Baechle 		{SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL},
314e01402b1SRalf Baechle 		{ARCH_SHF_SMALL | SHF_ALLOC, 0}
315e01402b1SRalf Baechle 	};
316e01402b1SRalf Baechle 	unsigned int m, i;
317e01402b1SRalf Baechle 
318e01402b1SRalf Baechle 	for (i = 0; i < hdr->e_shnum; i++)
319e01402b1SRalf Baechle 		sechdrs[i].sh_entsize = ~0UL;
320e01402b1SRalf Baechle 
321e01402b1SRalf Baechle 	for (m = 0; m < ARRAY_SIZE(masks); ++m) {
322e01402b1SRalf Baechle 		for (i = 0; i < hdr->e_shnum; ++i) {
323e01402b1SRalf Baechle 			Elf_Shdr *s = &sechdrs[i];
324e01402b1SRalf Baechle 
325e01402b1SRalf Baechle 			//  || strncmp(secstrings + s->sh_name, ".init", 5) == 0)
326e01402b1SRalf Baechle 			if ((s->sh_flags & masks[m][0]) != masks[m][0]
327e01402b1SRalf Baechle 			    || (s->sh_flags & masks[m][1])
328e01402b1SRalf Baechle 			    || s->sh_entsize != ~0UL)
329e01402b1SRalf Baechle 				continue;
330e2a9cf96SRaghu Gandham 			s->sh_entsize =
331e2a9cf96SRaghu Gandham 				get_offset((unsigned long *)&mod->core_size, s);
332e01402b1SRalf Baechle 		}
333e01402b1SRalf Baechle 
334e01402b1SRalf Baechle 		if (m == 0)
335e01402b1SRalf Baechle 			mod->core_text_size = mod->core_size;
336e01402b1SRalf Baechle 
337e01402b1SRalf Baechle 	}
338e01402b1SRalf Baechle }
339e01402b1SRalf Baechle 
340e01402b1SRalf Baechle 
341e01402b1SRalf Baechle /* from module-elf32.c, but subverted a little */
342e01402b1SRalf Baechle 
343e01402b1SRalf Baechle struct mips_hi16 {
344e01402b1SRalf Baechle 	struct mips_hi16 *next;
345e01402b1SRalf Baechle 	Elf32_Addr *addr;
346e01402b1SRalf Baechle 	Elf32_Addr value;
347e01402b1SRalf Baechle };
348e01402b1SRalf Baechle 
349e01402b1SRalf Baechle static struct mips_hi16 *mips_hi16_list;
350e01402b1SRalf Baechle static unsigned int gp_offs, gp_addr;
351e01402b1SRalf Baechle 
352e01402b1SRalf Baechle static int apply_r_mips_none(struct module *me, uint32_t *location,
353e01402b1SRalf Baechle 			     Elf32_Addr v)
354e01402b1SRalf Baechle {
355e01402b1SRalf Baechle 	return 0;
356e01402b1SRalf Baechle }
357e01402b1SRalf Baechle 
358e01402b1SRalf Baechle static int apply_r_mips_gprel16(struct module *me, uint32_t *location,
359e01402b1SRalf Baechle 				Elf32_Addr v)
360e01402b1SRalf Baechle {
361e01402b1SRalf Baechle 	int rel;
362e01402b1SRalf Baechle 
363e01402b1SRalf Baechle 	if( !(*location & 0xffff) ) {
364e01402b1SRalf Baechle 		rel = (int)v - gp_addr;
365e01402b1SRalf Baechle 	}
366e01402b1SRalf Baechle 	else {
367e01402b1SRalf Baechle 		/* .sbss + gp(relative) + offset */
368e01402b1SRalf Baechle 		/* kludge! */
369e01402b1SRalf Baechle 		rel =  (int)(short)((int)v + gp_offs +
370e01402b1SRalf Baechle 				    (int)(short)(*location & 0xffff) - gp_addr);
371e01402b1SRalf Baechle 	}
372e01402b1SRalf Baechle 
373e01402b1SRalf Baechle 	if( (rel > 32768) || (rel < -32768) ) {
3742600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: apply_r_mips_gprel16: "
3752600990eSRalf Baechle 		       "relative address 0x%x out of range of gp register\n",
3762600990eSRalf Baechle 		       rel);
377e01402b1SRalf Baechle 		return -ENOEXEC;
378e01402b1SRalf Baechle 	}
379e01402b1SRalf Baechle 
380e01402b1SRalf Baechle 	*location = (*location & 0xffff0000) | (rel & 0xffff);
381e01402b1SRalf Baechle 
382e01402b1SRalf Baechle 	return 0;
383e01402b1SRalf Baechle }
384e01402b1SRalf Baechle 
385e01402b1SRalf Baechle static int apply_r_mips_pc16(struct module *me, uint32_t *location,
386e01402b1SRalf Baechle 			     Elf32_Addr v)
387e01402b1SRalf Baechle {
388e01402b1SRalf Baechle 	int rel;
389e01402b1SRalf Baechle 	rel = (((unsigned int)v - (unsigned int)location));
390e01402b1SRalf Baechle 	rel >>= 2;		// because the offset is in _instructions_ not bytes.
391e01402b1SRalf Baechle 	rel -= 1;		// and one instruction less due to the branch delay slot.
392e01402b1SRalf Baechle 
393e01402b1SRalf Baechle 	if( (rel > 32768) || (rel < -32768) ) {
3942600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: "
395e01402b1SRalf Baechle  		       "apply_r_mips_pc16: relative address out of range 0x%x\n", rel);
396e01402b1SRalf Baechle 		return -ENOEXEC;
397e01402b1SRalf Baechle 	}
398e01402b1SRalf Baechle 
399e01402b1SRalf Baechle 	*location = (*location & 0xffff0000) | (rel & 0xffff);
400e01402b1SRalf Baechle 
401e01402b1SRalf Baechle 	return 0;
402e01402b1SRalf Baechle }
403e01402b1SRalf Baechle 
404e01402b1SRalf Baechle static int apply_r_mips_32(struct module *me, uint32_t *location,
405e01402b1SRalf Baechle 			   Elf32_Addr v)
406e01402b1SRalf Baechle {
407e01402b1SRalf Baechle 	*location += v;
408e01402b1SRalf Baechle 
409e01402b1SRalf Baechle 	return 0;
410e01402b1SRalf Baechle }
411e01402b1SRalf Baechle 
412e01402b1SRalf Baechle static int apply_r_mips_26(struct module *me, uint32_t *location,
413e01402b1SRalf Baechle 			   Elf32_Addr v)
414e01402b1SRalf Baechle {
415e01402b1SRalf Baechle 	if (v % 4) {
4162600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: apply_r_mips_26 "
4172600990eSRalf Baechle 		       " unaligned relocation\n");
418e01402b1SRalf Baechle 		return -ENOEXEC;
419e01402b1SRalf Baechle 	}
420e01402b1SRalf Baechle 
421307bd284SRalf Baechle /*
422307bd284SRalf Baechle  * Not desperately convinced this is a good check of an overflow condition
423307bd284SRalf Baechle  * anyway. But it gets in the way of handling undefined weak symbols which
424307bd284SRalf Baechle  * we want to set to zero.
425307bd284SRalf Baechle  * if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) {
426307bd284SRalf Baechle  * printk(KERN_ERR
427307bd284SRalf Baechle  * "module %s: relocation overflow\n",
428307bd284SRalf Baechle  * me->name);
429307bd284SRalf Baechle  * return -ENOEXEC;
430307bd284SRalf Baechle  * }
431e01402b1SRalf Baechle  */
432e01402b1SRalf Baechle 
433e01402b1SRalf Baechle 	*location = (*location & ~0x03ffffff) |
434e01402b1SRalf Baechle 		((*location + (v >> 2)) & 0x03ffffff);
435e01402b1SRalf Baechle 	return 0;
436e01402b1SRalf Baechle }
437e01402b1SRalf Baechle 
438e01402b1SRalf Baechle static int apply_r_mips_hi16(struct module *me, uint32_t *location,
439e01402b1SRalf Baechle 			     Elf32_Addr v)
440e01402b1SRalf Baechle {
441e01402b1SRalf Baechle 	struct mips_hi16 *n;
442e01402b1SRalf Baechle 
443e01402b1SRalf Baechle 	/*
444e01402b1SRalf Baechle 	 * We cannot relocate this one now because we don't know the value of
445e01402b1SRalf Baechle 	 * the carry we need to add.  Save the information, and let LO16 do the
446e01402b1SRalf Baechle 	 * actual relocation.
447e01402b1SRalf Baechle 	 */
448e01402b1SRalf Baechle 	n = kmalloc(sizeof *n, GFP_KERNEL);
449e01402b1SRalf Baechle 	if (!n)
450e01402b1SRalf Baechle 		return -ENOMEM;
451e01402b1SRalf Baechle 
452e01402b1SRalf Baechle 	n->addr = location;
453e01402b1SRalf Baechle 	n->value = v;
454e01402b1SRalf Baechle 	n->next = mips_hi16_list;
455e01402b1SRalf Baechle 	mips_hi16_list = n;
456e01402b1SRalf Baechle 
457e01402b1SRalf Baechle 	return 0;
458e01402b1SRalf Baechle }
459e01402b1SRalf Baechle 
460e01402b1SRalf Baechle static int apply_r_mips_lo16(struct module *me, uint32_t *location,
461e01402b1SRalf Baechle 			     Elf32_Addr v)
462e01402b1SRalf Baechle {
463e01402b1SRalf Baechle 	unsigned long insnlo = *location;
464e01402b1SRalf Baechle 	Elf32_Addr val, vallo;
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 		struct mips_hi16 *l;
471e01402b1SRalf Baechle 
472e01402b1SRalf Baechle 		l = mips_hi16_list;
473e01402b1SRalf Baechle 		while (l != NULL) {
474e01402b1SRalf Baechle 			struct mips_hi16 *next;
475e01402b1SRalf Baechle 			unsigned long insn;
476e01402b1SRalf Baechle 
477e01402b1SRalf Baechle 			/*
478e01402b1SRalf Baechle 			 * The value for the HI16 had best be the same.
479e01402b1SRalf Baechle 			 */
480e01402b1SRalf Baechle  			if (v != l->value) {
4812600990eSRalf Baechle 				printk(KERN_DEBUG "VPE loader: "
482b1e3afa0SJoe Perches 				       "apply_r_mips_lo16/hi16: \t"
4832600990eSRalf Baechle 				       "inconsistent value information\n");
4842600990eSRalf Baechle 				return -ENOEXEC;
485e01402b1SRalf Baechle 			}
486e01402b1SRalf Baechle 
487e01402b1SRalf Baechle 			/*
488e01402b1SRalf Baechle 			 * Do the HI16 relocation.  Note that we actually don't
489e01402b1SRalf Baechle 			 * need to know anything about the LO16 itself, except
490e01402b1SRalf Baechle 			 * where to find the low 16 bits of the addend needed
491e01402b1SRalf Baechle 			 * by the LO16.
492e01402b1SRalf Baechle 			 */
493e01402b1SRalf Baechle 			insn = *l->addr;
494e01402b1SRalf Baechle 			val = ((insn & 0xffff) << 16) + vallo;
495e01402b1SRalf Baechle 			val += v;
496e01402b1SRalf Baechle 
497e01402b1SRalf Baechle 			/*
498e01402b1SRalf Baechle 			 * Account for the sign extension that will happen in
499e01402b1SRalf Baechle 			 * the low bits.
500e01402b1SRalf Baechle 			 */
501e01402b1SRalf Baechle 			val = ((val >> 16) + ((val & 0x8000) != 0)) & 0xffff;
502e01402b1SRalf Baechle 
503e01402b1SRalf Baechle 			insn = (insn & ~0xffff) | val;
504e01402b1SRalf Baechle 			*l->addr = insn;
505e01402b1SRalf Baechle 
506e01402b1SRalf Baechle 			next = l->next;
507e01402b1SRalf Baechle 			kfree(l);
508e01402b1SRalf Baechle 			l = next;
509e01402b1SRalf Baechle 		}
510e01402b1SRalf Baechle 
511e01402b1SRalf Baechle 		mips_hi16_list = NULL;
512e01402b1SRalf Baechle 	}
513e01402b1SRalf Baechle 
514e01402b1SRalf Baechle 	/*
515e01402b1SRalf Baechle 	 * Ok, we're done with the HI16 relocs.  Now deal with the LO16.
516e01402b1SRalf Baechle 	 */
517e01402b1SRalf Baechle 	val = v + vallo;
518e01402b1SRalf Baechle 	insnlo = (insnlo & ~0xffff) | (val & 0xffff);
519e01402b1SRalf Baechle 	*location = insnlo;
520e01402b1SRalf Baechle 
521e01402b1SRalf Baechle 	return 0;
522e01402b1SRalf Baechle }
523e01402b1SRalf Baechle 
524e01402b1SRalf Baechle static int (*reloc_handlers[]) (struct module *me, uint32_t *location,
525e01402b1SRalf Baechle 				Elf32_Addr v) = {
526e01402b1SRalf Baechle 	[R_MIPS_NONE]	= apply_r_mips_none,
527e01402b1SRalf Baechle 	[R_MIPS_32]	= apply_r_mips_32,
528e01402b1SRalf Baechle 	[R_MIPS_26]	= apply_r_mips_26,
529e01402b1SRalf Baechle 	[R_MIPS_HI16]	= apply_r_mips_hi16,
530e01402b1SRalf Baechle 	[R_MIPS_LO16]	= apply_r_mips_lo16,
531e01402b1SRalf Baechle 	[R_MIPS_GPREL16] = apply_r_mips_gprel16,
532e01402b1SRalf Baechle 	[R_MIPS_PC16] = apply_r_mips_pc16
533e01402b1SRalf Baechle };
534e01402b1SRalf Baechle 
5352600990eSRalf Baechle static char *rstrs[] = {
5362600990eSRalf Baechle 	[R_MIPS_NONE]	= "MIPS_NONE",
5372600990eSRalf Baechle 	[R_MIPS_32]	= "MIPS_32",
5382600990eSRalf Baechle 	[R_MIPS_26]	= "MIPS_26",
5392600990eSRalf Baechle 	[R_MIPS_HI16]	= "MIPS_HI16",
5402600990eSRalf Baechle 	[R_MIPS_LO16]	= "MIPS_LO16",
5412600990eSRalf Baechle 	[R_MIPS_GPREL16] = "MIPS_GPREL16",
5422600990eSRalf Baechle 	[R_MIPS_PC16] = "MIPS_PC16"
5432600990eSRalf Baechle };
544e01402b1SRalf Baechle 
545e01402b1SRalf Baechle int apply_relocations(Elf32_Shdr *sechdrs,
546e01402b1SRalf Baechle 		      const char *strtab,
547e01402b1SRalf Baechle 		      unsigned int symindex,
548e01402b1SRalf Baechle 		      unsigned int relsec,
549e01402b1SRalf Baechle 		      struct module *me)
550e01402b1SRalf Baechle {
551e01402b1SRalf Baechle 	Elf32_Rel *rel = (void *) sechdrs[relsec].sh_addr;
552e01402b1SRalf Baechle 	Elf32_Sym *sym;
553e01402b1SRalf Baechle 	uint32_t *location;
554e01402b1SRalf Baechle 	unsigned int i;
555e01402b1SRalf Baechle 	Elf32_Addr v;
556e01402b1SRalf Baechle 	int res;
557e01402b1SRalf Baechle 
558e01402b1SRalf Baechle 	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
559e01402b1SRalf Baechle 		Elf32_Word r_info = rel[i].r_info;
560e01402b1SRalf Baechle 
561e01402b1SRalf Baechle 		/* This is where to make the change */
562e01402b1SRalf Baechle 		location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
563e01402b1SRalf Baechle 			+ rel[i].r_offset;
564e01402b1SRalf Baechle 		/* This is the symbol it is referring to */
565e01402b1SRalf Baechle 		sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
566e01402b1SRalf Baechle 			+ ELF32_R_SYM(r_info);
567e01402b1SRalf Baechle 
568e01402b1SRalf Baechle 		if (!sym->st_value) {
569e01402b1SRalf Baechle 			printk(KERN_DEBUG "%s: undefined weak symbol %s\n",
570e01402b1SRalf Baechle 			       me->name, strtab + sym->st_name);
571e01402b1SRalf Baechle 			/* just print the warning, dont barf */
572e01402b1SRalf Baechle 		}
573e01402b1SRalf Baechle 
574e01402b1SRalf Baechle 		v = sym->st_value;
575e01402b1SRalf Baechle 
576e01402b1SRalf Baechle 		res = reloc_handlers[ELF32_R_TYPE(r_info)](me, location, v);
577e01402b1SRalf Baechle 		if( res ) {
5782600990eSRalf Baechle 			char *r = rstrs[ELF32_R_TYPE(r_info)];
5792600990eSRalf Baechle 		    	printk(KERN_WARNING "VPE loader: .text+0x%x "
5802600990eSRalf Baechle 			       "relocation type %s for symbol \"%s\" failed\n",
5812600990eSRalf Baechle 			       rel[i].r_offset, r ? r : "UNKNOWN",
5822600990eSRalf Baechle 			       strtab + sym->st_name);
583e01402b1SRalf Baechle 			return res;
584e01402b1SRalf Baechle 		}
5852600990eSRalf Baechle 	}
586e01402b1SRalf Baechle 
587e01402b1SRalf Baechle 	return 0;
588e01402b1SRalf Baechle }
589e01402b1SRalf Baechle 
590e01402b1SRalf Baechle void save_gp_address(unsigned int secbase, unsigned int rel)
591e01402b1SRalf Baechle {
592e01402b1SRalf Baechle 	gp_addr = secbase + rel;
593e01402b1SRalf Baechle 	gp_offs = gp_addr - (secbase & 0xffff0000);
594e01402b1SRalf Baechle }
595e01402b1SRalf Baechle /* end module-elf32.c */
596e01402b1SRalf Baechle 
597e01402b1SRalf Baechle 
598e01402b1SRalf Baechle 
599e01402b1SRalf Baechle /* Change all symbols so that sh_value encodes the pointer directly. */
6002600990eSRalf Baechle static void simplify_symbols(Elf_Shdr * sechdrs,
601e01402b1SRalf Baechle 			    unsigned int symindex,
602e01402b1SRalf Baechle 			    const char *strtab,
603e01402b1SRalf Baechle 			    const char *secstrings,
604e01402b1SRalf Baechle 			    unsigned int nsecs, struct module *mod)
605e01402b1SRalf Baechle {
606e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
607e01402b1SRalf Baechle 	unsigned long secbase, bssbase = 0;
608e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
6092600990eSRalf Baechle 	int size;
610e01402b1SRalf Baechle 
611e01402b1SRalf Baechle 	/* find the .bss section for COMMON symbols */
612e01402b1SRalf Baechle 	for (i = 0; i < nsecs; i++) {
6132600990eSRalf Baechle 		if (strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) == 0) {
614e01402b1SRalf Baechle 			bssbase = sechdrs[i].sh_addr;
6152600990eSRalf Baechle 			break;
6162600990eSRalf Baechle 		}
617e01402b1SRalf Baechle 	}
618e01402b1SRalf Baechle 
619e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
620e01402b1SRalf Baechle 		switch (sym[i].st_shndx) {
621e01402b1SRalf Baechle 		case SHN_COMMON:
6222600990eSRalf Baechle 			/* Allocate space for the symbol in the .bss section.
6232600990eSRalf Baechle 			   st_value is currently size.
624e01402b1SRalf Baechle 			   We want it to have the address of the symbol. */
625e01402b1SRalf Baechle 
626e01402b1SRalf Baechle 			size = sym[i].st_value;
627e01402b1SRalf Baechle 			sym[i].st_value = bssbase;
628e01402b1SRalf Baechle 
629e01402b1SRalf Baechle 			bssbase += size;
630e01402b1SRalf Baechle 			break;
631e01402b1SRalf Baechle 
632e01402b1SRalf Baechle 		case SHN_ABS:
633e01402b1SRalf Baechle 			/* Don't need to do anything */
634e01402b1SRalf Baechle 			break;
635e01402b1SRalf Baechle 
636e01402b1SRalf Baechle 		case SHN_UNDEF:
637e01402b1SRalf Baechle 			/* ret = -ENOENT; */
638e01402b1SRalf Baechle 			break;
639e01402b1SRalf Baechle 
640e01402b1SRalf Baechle 		case SHN_MIPS_SCOMMON:
6412600990eSRalf Baechle 			printk(KERN_DEBUG "simplify_symbols: ignoring SHN_MIPS_SCOMMON "
6422600990eSRalf Baechle 			       "symbol <%s> st_shndx %d\n", strtab + sym[i].st_name,
6432600990eSRalf Baechle 			       sym[i].st_shndx);
644e01402b1SRalf Baechle 			// .sbss section
645e01402b1SRalf Baechle 			break;
646e01402b1SRalf Baechle 
647e01402b1SRalf Baechle 		default:
648e01402b1SRalf Baechle 			secbase = sechdrs[sym[i].st_shndx].sh_addr;
649e01402b1SRalf Baechle 
650e01402b1SRalf Baechle 			if (strncmp(strtab + sym[i].st_name, "_gp", 3) == 0) {
651e01402b1SRalf Baechle 				save_gp_address(secbase, sym[i].st_value);
652e01402b1SRalf Baechle 			}
653e01402b1SRalf Baechle 
654e01402b1SRalf Baechle 			sym[i].st_value += secbase;
655e01402b1SRalf Baechle 			break;
656e01402b1SRalf Baechle 		}
657e01402b1SRalf Baechle 	}
658e01402b1SRalf Baechle }
659e01402b1SRalf Baechle 
660e01402b1SRalf Baechle #ifdef DEBUG_ELFLOADER
661e01402b1SRalf Baechle static void dump_elfsymbols(Elf_Shdr * sechdrs, unsigned int symindex,
662e01402b1SRalf Baechle 			    const char *strtab, struct module *mod)
663e01402b1SRalf Baechle {
664e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
665e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
666e01402b1SRalf Baechle 
667e01402b1SRalf Baechle 	printk(KERN_DEBUG "dump_elfsymbols: n %d\n", n);
668e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
669e01402b1SRalf Baechle 		printk(KERN_DEBUG " i %d name <%s> 0x%x\n", i,
670e01402b1SRalf Baechle 		       strtab + sym[i].st_name, sym[i].st_value);
671e01402b1SRalf Baechle 	}
672e01402b1SRalf Baechle }
673e01402b1SRalf Baechle #endif
674e01402b1SRalf Baechle 
675e01402b1SRalf Baechle /* We are prepared so configure and start the VPE... */
676be6e1437SRalf Baechle static int vpe_run(struct vpe * v)
677e01402b1SRalf Baechle {
67807cc0c9eSRalf Baechle 	unsigned long flags, val, dmt_flag;
6792600990eSRalf Baechle 	struct vpe_notifications *n;
68007cc0c9eSRalf Baechle 	unsigned int vpeflags;
681e01402b1SRalf Baechle 	struct tc *t;
682e01402b1SRalf Baechle 
683e01402b1SRalf Baechle 	/* check we are the Master VPE */
68407cc0c9eSRalf Baechle 	local_irq_save(flags);
685e01402b1SRalf Baechle 	val = read_c0_vpeconf0();
686e01402b1SRalf Baechle 	if (!(val & VPECONF0_MVP)) {
687e01402b1SRalf Baechle 		printk(KERN_WARNING
6882600990eSRalf Baechle 		       "VPE loader: only Master VPE's are allowed to configure MT\n");
68907cc0c9eSRalf Baechle 		local_irq_restore(flags);
69007cc0c9eSRalf Baechle 
691e01402b1SRalf Baechle 		return -1;
692e01402b1SRalf Baechle 	}
693e01402b1SRalf Baechle 
69407cc0c9eSRalf Baechle 	dmt_flag = dmt();
69507cc0c9eSRalf Baechle 	vpeflags = dvpe();
696e01402b1SRalf Baechle 
697e01402b1SRalf Baechle 	if (!list_empty(&v->tc)) {
698e01402b1SRalf Baechle 		if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
69907cc0c9eSRalf Baechle 			evpe(vpeflags);
70007cc0c9eSRalf Baechle 			emt(dmt_flag);
70107cc0c9eSRalf Baechle 			local_irq_restore(flags);
70207cc0c9eSRalf Baechle 
70307cc0c9eSRalf Baechle 			printk(KERN_WARNING
70407cc0c9eSRalf Baechle 			       "VPE loader: TC %d is already in use.\n",
705e01402b1SRalf Baechle                                t->index);
706e01402b1SRalf Baechle 			return -ENOEXEC;
707e01402b1SRalf Baechle 		}
708e01402b1SRalf Baechle 	} else {
70907cc0c9eSRalf Baechle 		evpe(vpeflags);
71007cc0c9eSRalf Baechle 		emt(dmt_flag);
71107cc0c9eSRalf Baechle 		local_irq_restore(flags);
71207cc0c9eSRalf Baechle 
71307cc0c9eSRalf Baechle 		printk(KERN_WARNING
71407cc0c9eSRalf Baechle 		       "VPE loader: No TC's associated with VPE %d\n",
715e01402b1SRalf Baechle 		       v->minor);
71607cc0c9eSRalf Baechle 
717e01402b1SRalf Baechle 		return -ENOEXEC;
718e01402b1SRalf Baechle 	}
719e01402b1SRalf Baechle 
7202600990eSRalf Baechle 	/* Put MVPE's into 'configuration state' */
7212600990eSRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
722e01402b1SRalf Baechle 
7232600990eSRalf Baechle 	settc(t->index);
724e01402b1SRalf Baechle 
725e01402b1SRalf Baechle 	/* should check it is halted, and not activated */
726e01402b1SRalf Baechle 	if ((read_tc_c0_tcstatus() & TCSTATUS_A) || !(read_tc_c0_tchalt() & TCHALT_H)) {
72707cc0c9eSRalf Baechle 		evpe(vpeflags);
72807cc0c9eSRalf Baechle 		emt(dmt_flag);
72907cc0c9eSRalf Baechle 		local_irq_restore(flags);
73007cc0c9eSRalf Baechle 
73107cc0c9eSRalf Baechle 		printk(KERN_WARNING "VPE loader: TC %d is already active!\n",
732e01402b1SRalf Baechle 		       t->index);
73307cc0c9eSRalf Baechle 
734e01402b1SRalf Baechle 		return -ENOEXEC;
735e01402b1SRalf Baechle 	}
736e01402b1SRalf Baechle 
737e01402b1SRalf Baechle 	/* Write the address we want it to start running from in the TCPC register. */
738e01402b1SRalf Baechle 	write_tc_c0_tcrestart((unsigned long)v->__start);
739e01402b1SRalf Baechle 	write_tc_c0_tccontext((unsigned long)0);
74007cc0c9eSRalf Baechle 
7412600990eSRalf Baechle 	/*
7422600990eSRalf Baechle 	 * Mark the TC as activated, not interrupt exempt and not dynamically
7432600990eSRalf Baechle 	 * allocatable
7442600990eSRalf Baechle 	 */
745e01402b1SRalf Baechle 	val = read_tc_c0_tcstatus();
746e01402b1SRalf Baechle 	val = (val & ~(TCSTATUS_DA | TCSTATUS_IXMT)) | TCSTATUS_A;
747e01402b1SRalf Baechle 	write_tc_c0_tcstatus(val);
748e01402b1SRalf Baechle 
749e01402b1SRalf Baechle 	write_tc_c0_tchalt(read_tc_c0_tchalt() & ~TCHALT_H);
750e01402b1SRalf Baechle 
751e01402b1SRalf Baechle 	/*
752e01402b1SRalf Baechle 	 * The sde-kit passes 'memsize' to __start in $a3, so set something
7532600990eSRalf Baechle 	 * here...  Or set $a3 to zero and define DFLT_STACK_SIZE and
754e01402b1SRalf Baechle 	 * DFLT_HEAP_SIZE when you compile your program
755e01402b1SRalf Baechle 	 */
75641790e04SRalf Baechle 	mttgpr(6, v->ntcs);
7572600990eSRalf Baechle 	mttgpr(7, physical_memsize);
758e01402b1SRalf Baechle 
7592600990eSRalf Baechle 	/* set up VPE1 */
7602600990eSRalf Baechle 	/*
7612600990eSRalf Baechle 	 * bind the TC to VPE 1 as late as possible so we only have the final
7622600990eSRalf Baechle 	 * VPE registers to set up, and so an EJTAG probe can trigger on it
7632600990eSRalf Baechle 	 */
76407cc0c9eSRalf Baechle 	write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | 1);
7652600990eSRalf Baechle 
766a94d7020SElizabeth Oldham 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~(VPECONF0_VPA));
767a94d7020SElizabeth Oldham 
768a94d7020SElizabeth Oldham 	back_to_back_c0_hazard();
769a94d7020SElizabeth Oldham 
7702600990eSRalf Baechle 	/* Set up the XTC bit in vpeconf0 to point at our tc */
7712600990eSRalf Baechle 	write_vpe_c0_vpeconf0( (read_vpe_c0_vpeconf0() & ~(VPECONF0_XTC))
7722600990eSRalf Baechle 	                      | (t->index << VPECONF0_XTC_SHIFT));
7732600990eSRalf Baechle 
774a94d7020SElizabeth Oldham 	back_to_back_c0_hazard();
775a94d7020SElizabeth Oldham 
7762600990eSRalf Baechle 	/* enable this VPE */
7772600990eSRalf Baechle 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA);
778e01402b1SRalf Baechle 
779e01402b1SRalf Baechle 	/* clear out any left overs from a previous program */
7802600990eSRalf Baechle 	write_vpe_c0_status(0);
781e01402b1SRalf Baechle 	write_vpe_c0_cause(0);
782e01402b1SRalf Baechle 
783e01402b1SRalf Baechle 	/* take system out of configuration state */
784340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
785e01402b1SRalf Baechle 
786b618336aSKevin D. Kissell 	/*
787b618336aSKevin D. Kissell 	 * SMTC/SMVP kernels manage VPE enable independently,
788b618336aSKevin D. Kissell 	 * but uniprocessor kernels need to turn it on, even
789b618336aSKevin D. Kissell 	 * if that wasn't the pre-dvpe() state.
790b618336aSKevin D. Kissell 	 */
79107cc0c9eSRalf Baechle #ifdef CONFIG_SMP
79207cc0c9eSRalf Baechle 	evpe(vpeflags);
793b618336aSKevin D. Kissell #else
794b618336aSKevin D. Kissell 	evpe(EVPE_ENABLE);
79507cc0c9eSRalf Baechle #endif
79607cc0c9eSRalf Baechle 	emt(dmt_flag);
79707cc0c9eSRalf Baechle 	local_irq_restore(flags);
798e01402b1SRalf Baechle 
79907cc0c9eSRalf Baechle 	list_for_each_entry(n, &v->notify, list)
80007cc0c9eSRalf Baechle 		n->start(minor);
8012600990eSRalf Baechle 
802e01402b1SRalf Baechle 	return 0;
803e01402b1SRalf Baechle }
804e01402b1SRalf Baechle 
8052600990eSRalf Baechle static int find_vpe_symbols(struct vpe * v, Elf_Shdr * sechdrs,
806e01402b1SRalf Baechle 				      unsigned int symindex, const char *strtab,
807e01402b1SRalf Baechle 				      struct module *mod)
808e01402b1SRalf Baechle {
809e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
810e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
811e01402b1SRalf Baechle 
812e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
813e01402b1SRalf Baechle 		if (strcmp(strtab + sym[i].st_name, "__start") == 0) {
814e01402b1SRalf Baechle 			v->__start = sym[i].st_value;
815e01402b1SRalf Baechle 		}
816e01402b1SRalf Baechle 
817e01402b1SRalf Baechle 		if (strcmp(strtab + sym[i].st_name, "vpe_shared") == 0) {
818e01402b1SRalf Baechle 			v->shared_ptr = (void *)sym[i].st_value;
819e01402b1SRalf Baechle 		}
820e01402b1SRalf Baechle 	}
821e01402b1SRalf Baechle 
8222600990eSRalf Baechle 	if ( (v->__start == 0) || (v->shared_ptr == NULL))
8232600990eSRalf Baechle 		return -1;
8242600990eSRalf Baechle 
825e01402b1SRalf Baechle 	return 0;
826e01402b1SRalf Baechle }
827e01402b1SRalf Baechle 
828307bd284SRalf Baechle /*
8292600990eSRalf Baechle  * Allocates a VPE with some program code space(the load address), copies the
8302600990eSRalf Baechle  * contents of the program (p)buffer performing relocatations/etc, free's it
8312600990eSRalf Baechle  * when finished.
832e01402b1SRalf Baechle  */
833be6e1437SRalf Baechle static int vpe_elfload(struct vpe * v)
834e01402b1SRalf Baechle {
835e01402b1SRalf Baechle 	Elf_Ehdr *hdr;
836e01402b1SRalf Baechle 	Elf_Shdr *sechdrs;
837e01402b1SRalf Baechle 	long err = 0;
838e01402b1SRalf Baechle 	char *secstrings, *strtab = NULL;
8392600990eSRalf Baechle 	unsigned int len, i, symindex = 0, strindex = 0, relocate = 0;
840e01402b1SRalf Baechle 	struct module mod;	// so we can re-use the relocations code
841e01402b1SRalf Baechle 
842e01402b1SRalf Baechle 	memset(&mod, 0, sizeof(struct module));
8432600990eSRalf Baechle 	strcpy(mod.name, "VPE loader");
844e01402b1SRalf Baechle 
845e01402b1SRalf Baechle 	hdr = (Elf_Ehdr *) v->pbuffer;
846e01402b1SRalf Baechle 	len = v->plen;
847e01402b1SRalf Baechle 
848e01402b1SRalf Baechle 	/* Sanity checks against insmoding binaries or wrong arch,
849e01402b1SRalf Baechle 	   weird elf version */
850d303f4a1SCyrill Gorcunov 	if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0
8512600990eSRalf Baechle 	    || (hdr->e_type != ET_REL && hdr->e_type != ET_EXEC)
8522600990eSRalf Baechle 	    || !elf_check_arch(hdr)
853e01402b1SRalf Baechle 	    || hdr->e_shentsize != sizeof(*sechdrs)) {
854e01402b1SRalf Baechle 		printk(KERN_WARNING
8552600990eSRalf Baechle 		       "VPE loader: program wrong arch or weird elf version\n");
856e01402b1SRalf Baechle 
857e01402b1SRalf Baechle 		return -ENOEXEC;
858e01402b1SRalf Baechle 	}
859e01402b1SRalf Baechle 
8602600990eSRalf Baechle 	if (hdr->e_type == ET_REL)
8612600990eSRalf Baechle 		relocate = 1;
8622600990eSRalf Baechle 
863e01402b1SRalf Baechle 	if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) {
8642600990eSRalf Baechle 		printk(KERN_ERR "VPE loader: program length %u truncated\n",
8652600990eSRalf Baechle 		       len);
8662600990eSRalf Baechle 
867e01402b1SRalf Baechle 		return -ENOEXEC;
868e01402b1SRalf Baechle 	}
869e01402b1SRalf Baechle 
870e01402b1SRalf Baechle 	/* Convenience variables */
871e01402b1SRalf Baechle 	sechdrs = (void *)hdr + hdr->e_shoff;
872e01402b1SRalf Baechle 	secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
873e01402b1SRalf Baechle 	sechdrs[0].sh_addr = 0;
874e01402b1SRalf Baechle 
875e01402b1SRalf Baechle 	/* And these should exist, but gcc whinges if we don't init them */
876e01402b1SRalf Baechle 	symindex = strindex = 0;
877e01402b1SRalf Baechle 
8782600990eSRalf Baechle 	if (relocate) {
879e01402b1SRalf Baechle 		for (i = 1; i < hdr->e_shnum; i++) {
880e01402b1SRalf Baechle 			if (sechdrs[i].sh_type != SHT_NOBITS
881e01402b1SRalf Baechle 			    && len < sechdrs[i].sh_offset + sechdrs[i].sh_size) {
882e01402b1SRalf Baechle 				printk(KERN_ERR "VPE program length %u truncated\n",
883e01402b1SRalf Baechle 				       len);
884e01402b1SRalf Baechle 				return -ENOEXEC;
885e01402b1SRalf Baechle 			}
886e01402b1SRalf Baechle 
887e01402b1SRalf Baechle 			/* Mark all sections sh_addr with their address in the
888e01402b1SRalf Baechle 			   temporary image. */
889e01402b1SRalf Baechle 			sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
890e01402b1SRalf Baechle 
891e01402b1SRalf Baechle 			/* Internal symbols and strings. */
892e01402b1SRalf Baechle 			if (sechdrs[i].sh_type == SHT_SYMTAB) {
893e01402b1SRalf Baechle 				symindex = i;
894e01402b1SRalf Baechle 				strindex = sechdrs[i].sh_link;
895e01402b1SRalf Baechle 				strtab = (char *)hdr + sechdrs[strindex].sh_offset;
896e01402b1SRalf Baechle 			}
897e01402b1SRalf Baechle 		}
898e01402b1SRalf Baechle 		layout_sections(&mod, hdr, sechdrs, secstrings);
8992600990eSRalf Baechle 	}
900e01402b1SRalf Baechle 
901e01402b1SRalf Baechle 	v->load_addr = alloc_progmem(mod.core_size);
9025408c490SRalf Baechle 	if (!v->load_addr)
9035408c490SRalf Baechle 		return -ENOMEM;
904e01402b1SRalf Baechle 
9055408c490SRalf Baechle 	pr_info("VPE loader: loading to %p\n", v->load_addr);
906e01402b1SRalf Baechle 
9072600990eSRalf Baechle 	if (relocate) {
908e01402b1SRalf Baechle 		for (i = 0; i < hdr->e_shnum; i++) {
909e01402b1SRalf Baechle 			void *dest;
910e01402b1SRalf Baechle 
911e01402b1SRalf Baechle 			if (!(sechdrs[i].sh_flags & SHF_ALLOC))
912e01402b1SRalf Baechle 				continue;
913e01402b1SRalf Baechle 
914e01402b1SRalf Baechle 			dest = v->load_addr + sechdrs[i].sh_entsize;
915e01402b1SRalf Baechle 
916e01402b1SRalf Baechle 			if (sechdrs[i].sh_type != SHT_NOBITS)
917e01402b1SRalf Baechle 				memcpy(dest, (void *)sechdrs[i].sh_addr,
918e01402b1SRalf Baechle 				       sechdrs[i].sh_size);
919e01402b1SRalf Baechle 			/* Update sh_addr to point to copy in image. */
920e01402b1SRalf Baechle 			sechdrs[i].sh_addr = (unsigned long)dest;
9212600990eSRalf Baechle 
9222600990eSRalf Baechle 			printk(KERN_DEBUG " section sh_name %s sh_addr 0x%x\n",
9232600990eSRalf Baechle 			       secstrings + sechdrs[i].sh_name, sechdrs[i].sh_addr);
924e01402b1SRalf Baechle 		}
925e01402b1SRalf Baechle 
926e01402b1SRalf Baechle  		/* Fix up syms, so that st_value is a pointer to location. */
927e01402b1SRalf Baechle  		simplify_symbols(sechdrs, symindex, strtab, secstrings,
928e01402b1SRalf Baechle  				 hdr->e_shnum, &mod);
929e01402b1SRalf Baechle 
930e01402b1SRalf Baechle  		/* Now do relocations. */
931e01402b1SRalf Baechle  		for (i = 1; i < hdr->e_shnum; i++) {
932e01402b1SRalf Baechle  			const char *strtab = (char *)sechdrs[strindex].sh_addr;
933e01402b1SRalf Baechle  			unsigned int info = sechdrs[i].sh_info;
934e01402b1SRalf Baechle 
935e01402b1SRalf Baechle  			/* Not a valid relocation section? */
936e01402b1SRalf Baechle  			if (info >= hdr->e_shnum)
937e01402b1SRalf Baechle  				continue;
938e01402b1SRalf Baechle 
939e01402b1SRalf Baechle  			/* Don't bother with non-allocated sections */
940e01402b1SRalf Baechle  			if (!(sechdrs[info].sh_flags & SHF_ALLOC))
941e01402b1SRalf Baechle  				continue;
942e01402b1SRalf Baechle 
943e01402b1SRalf Baechle  			if (sechdrs[i].sh_type == SHT_REL)
9442600990eSRalf Baechle  				err = apply_relocations(sechdrs, strtab, symindex, i,
9452600990eSRalf Baechle  							&mod);
946e01402b1SRalf Baechle  			else if (sechdrs[i].sh_type == SHT_RELA)
947e01402b1SRalf Baechle  				err = apply_relocate_add(sechdrs, strtab, symindex, i,
948e01402b1SRalf Baechle  							 &mod);
9492600990eSRalf Baechle  			if (err < 0)
9502600990eSRalf Baechle  				return err;
9512600990eSRalf Baechle 
9522600990eSRalf Baechle   		}
9532600990eSRalf Baechle   	} else {
954bdf5d42cSRalf Baechle 		struct elf_phdr *phdr = (struct elf_phdr *) ((char *)hdr + hdr->e_phoff);
9552600990eSRalf Baechle 
956bdf5d42cSRalf Baechle 		for (i = 0; i < hdr->e_phnum; i++) {
957b618336aSKevin D. Kissell 			if (phdr->p_type == PT_LOAD) {
958b618336aSKevin D. Kissell 				memcpy((void *)phdr->p_paddr,
959b618336aSKevin D. Kissell 				       (char *)hdr + phdr->p_offset,
960b618336aSKevin D. Kissell 				       phdr->p_filesz);
961b618336aSKevin D. Kissell 				memset((void *)phdr->p_paddr + phdr->p_filesz,
962b618336aSKevin D. Kissell 				       0, phdr->p_memsz - phdr->p_filesz);
963b618336aSKevin D. Kissell 		    }
964bdf5d42cSRalf Baechle 		    phdr++;
965bdf5d42cSRalf Baechle 		}
966bdf5d42cSRalf Baechle 
967bdf5d42cSRalf Baechle 		for (i = 0; i < hdr->e_shnum; i++) {
9682600990eSRalf Baechle  			/* Internal symbols and strings. */
9692600990eSRalf Baechle  			if (sechdrs[i].sh_type == SHT_SYMTAB) {
9702600990eSRalf Baechle  				symindex = i;
9712600990eSRalf Baechle  				strindex = sechdrs[i].sh_link;
9722600990eSRalf Baechle  				strtab = (char *)hdr + sechdrs[strindex].sh_offset;
9732600990eSRalf Baechle 
9742600990eSRalf Baechle  				/* mark the symtab's address for when we try to find the
9752600990eSRalf Baechle  				   magic symbols */
9762600990eSRalf Baechle  				sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
9772600990eSRalf Baechle  			}
978e01402b1SRalf Baechle 		}
979e01402b1SRalf Baechle 	}
980e01402b1SRalf Baechle 
981e01402b1SRalf Baechle 	/* make sure it's physically written out */
982e01402b1SRalf Baechle 	flush_icache_range((unsigned long)v->load_addr,
983e01402b1SRalf Baechle 			   (unsigned long)v->load_addr + v->len);
984e01402b1SRalf Baechle 
985e01402b1SRalf Baechle 	if ((find_vpe_symbols(v, sechdrs, symindex, strtab, &mod)) < 0) {
9862600990eSRalf Baechle 		if (v->__start == 0) {
9872600990eSRalf Baechle 			printk(KERN_WARNING "VPE loader: program does not contain "
9882600990eSRalf Baechle 			       "a __start symbol\n");
9892600990eSRalf Baechle 			return -ENOEXEC;
9902600990eSRalf Baechle 		}
991e01402b1SRalf Baechle 
9922600990eSRalf Baechle 		if (v->shared_ptr == NULL)
9932600990eSRalf Baechle 			printk(KERN_WARNING "VPE loader: "
9942600990eSRalf Baechle 			       "program does not contain vpe_shared symbol.\n"
9952600990eSRalf Baechle 			       " Unable to use AMVP (AP/SP) facilities.\n");
996e01402b1SRalf Baechle 	}
997e01402b1SRalf Baechle 
998e01402b1SRalf Baechle 	printk(" elf loaded\n");
9992600990eSRalf Baechle 	return 0;
1000e01402b1SRalf Baechle }
1001e01402b1SRalf Baechle 
10022600990eSRalf Baechle static void cleanup_tc(struct tc *tc)
1003e01402b1SRalf Baechle {
100407cc0c9eSRalf Baechle 	unsigned long flags;
100507cc0c9eSRalf Baechle 	unsigned int mtflags, vpflags;
10062600990eSRalf Baechle 	int tmp;
1007e01402b1SRalf Baechle 
100807cc0c9eSRalf Baechle 	local_irq_save(flags);
100907cc0c9eSRalf Baechle 	mtflags = dmt();
101007cc0c9eSRalf Baechle 	vpflags = dvpe();
10112600990eSRalf Baechle 	/* Put MVPE's into 'configuration state' */
10122600990eSRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1013e01402b1SRalf Baechle 
10142600990eSRalf Baechle 	settc(tc->index);
1015e01402b1SRalf Baechle 	tmp = read_tc_c0_tcstatus();
1016e01402b1SRalf Baechle 
1017e01402b1SRalf Baechle 	/* mark not allocated and not dynamically allocatable */
1018e01402b1SRalf Baechle 	tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1019e01402b1SRalf Baechle 	tmp |= TCSTATUS_IXMT;	/* interrupt exempt */
1020e01402b1SRalf Baechle 	write_tc_c0_tcstatus(tmp);
1021e01402b1SRalf Baechle 
1022e01402b1SRalf Baechle 	write_tc_c0_tchalt(TCHALT_H);
10237c3a622dSNigel Stephens 	mips_ihb();
1024e01402b1SRalf Baechle 
10252600990eSRalf Baechle 	/* bind it to anything other than VPE1 */
102607cc0c9eSRalf Baechle //	write_tc_c0_tcbind(read_tc_c0_tcbind() & ~TCBIND_CURVPE); // | TCBIND_CURVPE
10272600990eSRalf Baechle 
10282600990eSRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
102907cc0c9eSRalf Baechle 	evpe(vpflags);
103007cc0c9eSRalf Baechle 	emt(mtflags);
103107cc0c9eSRalf Baechle 	local_irq_restore(flags);
10322600990eSRalf Baechle }
10332600990eSRalf Baechle 
10342600990eSRalf Baechle static int getcwd(char *buff, int size)
10352600990eSRalf Baechle {
10362600990eSRalf Baechle 	mm_segment_t old_fs;
10372600990eSRalf Baechle 	int ret;
10382600990eSRalf Baechle 
10392600990eSRalf Baechle 	old_fs = get_fs();
10402600990eSRalf Baechle 	set_fs(KERNEL_DS);
10412600990eSRalf Baechle 
10422600990eSRalf Baechle 	ret = sys_getcwd(buff, size);
10432600990eSRalf Baechle 
10442600990eSRalf Baechle 	set_fs(old_fs);
10452600990eSRalf Baechle 
10462600990eSRalf Baechle 	return ret;
10472600990eSRalf Baechle }
10482600990eSRalf Baechle 
10492600990eSRalf Baechle /* checks VPE is unused and gets ready to load program  */
10502600990eSRalf Baechle static int vpe_open(struct inode *inode, struct file *filp)
10512600990eSRalf Baechle {
1052c4c4018bSRalf Baechle 	enum vpe_state state;
10532600990eSRalf Baechle 	struct vpe_notifications *not;
105407cc0c9eSRalf Baechle 	struct vpe *v;
10557558da94SJonathan Corbet 	int ret, err = 0;
10562600990eSRalf Baechle 
10577558da94SJonathan Corbet 	lock_kernel();
105807cc0c9eSRalf Baechle 	if (minor != iminor(inode)) {
105907cc0c9eSRalf Baechle 		/* assume only 1 device at the moment. */
10602600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: only vpe1 is supported\n");
10617558da94SJonathan Corbet 		err = -ENODEV;
10627558da94SJonathan Corbet 		goto out;
10632600990eSRalf Baechle 	}
10642600990eSRalf Baechle 
106507cc0c9eSRalf Baechle 	if ((v = get_vpe(tclimit)) == NULL) {
10662600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: unable to get vpe\n");
10677558da94SJonathan Corbet 		err = -ENODEV;
10687558da94SJonathan Corbet 		goto out;
10692600990eSRalf Baechle 	}
10702600990eSRalf Baechle 
1071c4c4018bSRalf Baechle 	state = xchg(&v->state, VPE_STATE_INUSE);
1072c4c4018bSRalf Baechle 	if (state != VPE_STATE_UNUSED) {
10732600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: tc in use dumping regs\n");
10742600990eSRalf Baechle 
10752600990eSRalf Baechle 		list_for_each_entry(not, &v->notify, list) {
107607cc0c9eSRalf Baechle 			not->stop(tclimit);
10772600990eSRalf Baechle 		}
10782600990eSRalf Baechle 
10792600990eSRalf Baechle 		release_progmem(v->load_addr);
108007cc0c9eSRalf Baechle 		cleanup_tc(get_tc(tclimit));
1081e01402b1SRalf Baechle 	}
1082e01402b1SRalf Baechle 
1083e01402b1SRalf Baechle 	/* this of-course trashes what was there before... */
1084e01402b1SRalf Baechle 	v->pbuffer = vmalloc(P_SIZE);
1085e01402b1SRalf Baechle 	v->plen = P_SIZE;
1086e01402b1SRalf Baechle 	v->load_addr = NULL;
1087e01402b1SRalf Baechle 	v->len = 0;
1088e01402b1SRalf Baechle 
1089d76b0d9bSDavid Howells 	v->uid = filp->f_cred->fsuid;
1090d76b0d9bSDavid Howells 	v->gid = filp->f_cred->fsgid;
10912600990eSRalf Baechle 
10922600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
10932600990eSRalf Baechle 	/* get kspd to tell us when a syscall_exit happens */
10942600990eSRalf Baechle 	if (!kspd_events_reqd) {
10952600990eSRalf Baechle 		kspd_notify(&kspd_events);
10962600990eSRalf Baechle 		kspd_events_reqd++;
10972600990eSRalf Baechle 	}
10982600990eSRalf Baechle #endif
10992600990eSRalf Baechle 
11002600990eSRalf Baechle 	v->cwd[0] = 0;
11012600990eSRalf Baechle 	ret = getcwd(v->cwd, VPE_PATH_MAX);
11022600990eSRalf Baechle 	if (ret < 0)
11032600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: open, getcwd returned %d\n", ret);
11042600990eSRalf Baechle 
11052600990eSRalf Baechle 	v->shared_ptr = NULL;
11062600990eSRalf Baechle 	v->__start = 0;
110707cc0c9eSRalf Baechle 
11087558da94SJonathan Corbet out:
11097558da94SJonathan Corbet 	unlock_kernel();
1110e01402b1SRalf Baechle 	return 0;
1111e01402b1SRalf Baechle }
1112e01402b1SRalf Baechle 
1113e01402b1SRalf Baechle static int vpe_release(struct inode *inode, struct file *filp)
1114e01402b1SRalf Baechle {
1115307bd284SRalf Baechle 	struct vpe *v;
1116e01402b1SRalf Baechle 	Elf_Ehdr *hdr;
111707cc0c9eSRalf Baechle 	int ret = 0;
1118e01402b1SRalf Baechle 
111907cc0c9eSRalf Baechle 	v = get_vpe(tclimit);
112007cc0c9eSRalf Baechle 	if (v == NULL)
1121e01402b1SRalf Baechle 		return -ENODEV;
1122e01402b1SRalf Baechle 
1123e01402b1SRalf Baechle 	hdr = (Elf_Ehdr *) v->pbuffer;
1124d303f4a1SCyrill Gorcunov 	if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) == 0) {
112507cc0c9eSRalf Baechle 		if (vpe_elfload(v) >= 0) {
1126e01402b1SRalf Baechle 			vpe_run(v);
112707cc0c9eSRalf Baechle 		} else {
11282600990eSRalf Baechle  			printk(KERN_WARNING "VPE loader: ELF load failed.\n");
1129e01402b1SRalf Baechle 			ret = -ENOEXEC;
1130e01402b1SRalf Baechle 		}
1131e01402b1SRalf Baechle 	} else {
11322600990eSRalf Baechle  		printk(KERN_WARNING "VPE loader: only elf files are supported\n");
1133e01402b1SRalf Baechle 		ret = -ENOEXEC;
1134e01402b1SRalf Baechle 	}
1135e01402b1SRalf Baechle 
11362600990eSRalf Baechle 	/* It's good to be able to run the SP and if it chokes have a look at
11372600990eSRalf Baechle 	   the /dev/rt?. But if we reset the pointer to the shared struct we
11388ebcfc8bSNick Andrew 	   lose what has happened. So perhaps if garbage is sent to the vpe
11392600990eSRalf Baechle 	   device, use it as a trigger for the reset. Hopefully a nice
11402600990eSRalf Baechle 	   executable will be along shortly. */
11412600990eSRalf Baechle 	if (ret < 0)
11422600990eSRalf Baechle 		v->shared_ptr = NULL;
11432600990eSRalf Baechle 
1144e01402b1SRalf Baechle 	// cleanup any temp buffers
1145e01402b1SRalf Baechle 	if (v->pbuffer)
1146e01402b1SRalf Baechle 		vfree(v->pbuffer);
1147e01402b1SRalf Baechle 	v->plen = 0;
1148e01402b1SRalf Baechle 	return ret;
1149e01402b1SRalf Baechle }
1150e01402b1SRalf Baechle 
1151e01402b1SRalf Baechle static ssize_t vpe_write(struct file *file, const char __user * buffer,
1152e01402b1SRalf Baechle 			 size_t count, loff_t * ppos)
1153e01402b1SRalf Baechle {
1154e01402b1SRalf Baechle 	size_t ret = count;
1155307bd284SRalf Baechle 	struct vpe *v;
1156e01402b1SRalf Baechle 
115707cc0c9eSRalf Baechle 	if (iminor(file->f_path.dentry->d_inode) != minor)
115807cc0c9eSRalf Baechle 		return -ENODEV;
115907cc0c9eSRalf Baechle 
116007cc0c9eSRalf Baechle 	v = get_vpe(tclimit);
116107cc0c9eSRalf Baechle 	if (v == NULL)
1162e01402b1SRalf Baechle 		return -ENODEV;
1163e01402b1SRalf Baechle 
1164e01402b1SRalf Baechle 	if (v->pbuffer == NULL) {
11652600990eSRalf Baechle 		printk(KERN_ERR "VPE loader: no buffer for program\n");
1166e01402b1SRalf Baechle 		return -ENOMEM;
1167e01402b1SRalf Baechle 	}
1168e01402b1SRalf Baechle 
1169e01402b1SRalf Baechle 	if ((count + v->len) > v->plen) {
1170e01402b1SRalf Baechle 		printk(KERN_WARNING
11712600990eSRalf Baechle 		       "VPE loader: elf size too big. Perhaps strip uneeded symbols\n");
1172e01402b1SRalf Baechle 		return -ENOMEM;
1173e01402b1SRalf Baechle 	}
1174e01402b1SRalf Baechle 
1175e01402b1SRalf Baechle 	count -= copy_from_user(v->pbuffer + v->len, buffer, count);
11762600990eSRalf Baechle 	if (!count)
1177e01402b1SRalf Baechle 		return -EFAULT;
1178e01402b1SRalf Baechle 
1179e01402b1SRalf Baechle 	v->len += count;
1180e01402b1SRalf Baechle 	return ret;
1181e01402b1SRalf Baechle }
1182e01402b1SRalf Baechle 
11835dfe4c96SArjan van de Ven static const struct file_operations vpe_fops = {
1184e01402b1SRalf Baechle 	.owner = THIS_MODULE,
1185e01402b1SRalf Baechle 	.open = vpe_open,
1186e01402b1SRalf Baechle 	.release = vpe_release,
1187e01402b1SRalf Baechle 	.write = vpe_write
1188e01402b1SRalf Baechle };
1189e01402b1SRalf Baechle 
1190e01402b1SRalf Baechle /* module wrapper entry points */
1191e01402b1SRalf Baechle /* give me a vpe */
1192e01402b1SRalf Baechle vpe_handle vpe_alloc(void)
1193e01402b1SRalf Baechle {
1194e01402b1SRalf Baechle 	int i;
1195e01402b1SRalf Baechle 	struct vpe *v;
1196e01402b1SRalf Baechle 
1197e01402b1SRalf Baechle 	/* find a vpe */
1198e01402b1SRalf Baechle 	for (i = 1; i < MAX_VPES; i++) {
1199e01402b1SRalf Baechle 		if ((v = get_vpe(i)) != NULL) {
1200e01402b1SRalf Baechle 			v->state = VPE_STATE_INUSE;
1201e01402b1SRalf Baechle 			return v;
1202e01402b1SRalf Baechle 		}
1203e01402b1SRalf Baechle 	}
1204e01402b1SRalf Baechle 	return NULL;
1205e01402b1SRalf Baechle }
1206e01402b1SRalf Baechle 
1207e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_alloc);
1208e01402b1SRalf Baechle 
1209e01402b1SRalf Baechle /* start running from here */
1210e01402b1SRalf Baechle int vpe_start(vpe_handle vpe, unsigned long start)
1211e01402b1SRalf Baechle {
1212e01402b1SRalf Baechle 	struct vpe *v = vpe;
1213e01402b1SRalf Baechle 
1214e01402b1SRalf Baechle 	v->__start = start;
1215e01402b1SRalf Baechle 	return vpe_run(v);
1216e01402b1SRalf Baechle }
1217e01402b1SRalf Baechle 
1218e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_start);
1219e01402b1SRalf Baechle 
1220e01402b1SRalf Baechle /* halt it for now */
1221e01402b1SRalf Baechle int vpe_stop(vpe_handle vpe)
1222e01402b1SRalf Baechle {
1223e01402b1SRalf Baechle 	struct vpe *v = vpe;
1224e01402b1SRalf Baechle 	struct tc *t;
1225e01402b1SRalf Baechle 	unsigned int evpe_flags;
1226e01402b1SRalf Baechle 
1227e01402b1SRalf Baechle 	evpe_flags = dvpe();
1228e01402b1SRalf Baechle 
1229e01402b1SRalf Baechle 	if ((t = list_entry(v->tc.next, struct tc, tc)) != NULL) {
1230e01402b1SRalf Baechle 
1231e01402b1SRalf Baechle 		settc(t->index);
1232e01402b1SRalf Baechle 		write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
1233e01402b1SRalf Baechle 	}
1234e01402b1SRalf Baechle 
1235e01402b1SRalf Baechle 	evpe(evpe_flags);
1236e01402b1SRalf Baechle 
1237e01402b1SRalf Baechle 	return 0;
1238e01402b1SRalf Baechle }
1239e01402b1SRalf Baechle 
1240e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_stop);
1241e01402b1SRalf Baechle 
1242e01402b1SRalf Baechle /* I've done with it thank you */
1243e01402b1SRalf Baechle int vpe_free(vpe_handle vpe)
1244e01402b1SRalf Baechle {
1245e01402b1SRalf Baechle 	struct vpe *v = vpe;
1246e01402b1SRalf Baechle 	struct tc *t;
1247e01402b1SRalf Baechle 	unsigned int evpe_flags;
1248e01402b1SRalf Baechle 
1249e01402b1SRalf Baechle 	if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
1250e01402b1SRalf Baechle 		return -ENOEXEC;
1251e01402b1SRalf Baechle 	}
1252e01402b1SRalf Baechle 
1253e01402b1SRalf Baechle 	evpe_flags = dvpe();
1254e01402b1SRalf Baechle 
1255e01402b1SRalf Baechle 	/* Put MVPE's into 'configuration state' */
1256340ee4b9SRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1257e01402b1SRalf Baechle 
1258e01402b1SRalf Baechle 	settc(t->index);
1259e01402b1SRalf Baechle 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
1260e01402b1SRalf Baechle 
12617c3a622dSNigel Stephens 	/* halt the TC */
1262e01402b1SRalf Baechle 	write_tc_c0_tchalt(TCHALT_H);
12637c3a622dSNigel Stephens 	mips_ihb();
12647c3a622dSNigel Stephens 
12657c3a622dSNigel Stephens 	/* mark the TC unallocated */
12667c3a622dSNigel Stephens 	write_tc_c0_tcstatus(read_tc_c0_tcstatus() & ~TCSTATUS_A);
1267e01402b1SRalf Baechle 
1268e01402b1SRalf Baechle 	v->state = VPE_STATE_UNUSED;
1269e01402b1SRalf Baechle 
1270340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
1271e01402b1SRalf Baechle 	evpe(evpe_flags);
1272e01402b1SRalf Baechle 
1273e01402b1SRalf Baechle 	return 0;
1274e01402b1SRalf Baechle }
1275e01402b1SRalf Baechle 
1276e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_free);
1277e01402b1SRalf Baechle 
1278e01402b1SRalf Baechle void *vpe_get_shared(int index)
1279e01402b1SRalf Baechle {
1280e01402b1SRalf Baechle 	struct vpe *v;
1281e01402b1SRalf Baechle 
12822600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
1283e01402b1SRalf Baechle 		return NULL;
1284e01402b1SRalf Baechle 
1285e01402b1SRalf Baechle 	return v->shared_ptr;
1286e01402b1SRalf Baechle }
1287e01402b1SRalf Baechle 
1288e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_get_shared);
1289e01402b1SRalf Baechle 
12902600990eSRalf Baechle int vpe_getuid(int index)
12912600990eSRalf Baechle {
12922600990eSRalf Baechle 	struct vpe *v;
12932600990eSRalf Baechle 
12942600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
12952600990eSRalf Baechle 		return -1;
12962600990eSRalf Baechle 
12972600990eSRalf Baechle 	return v->uid;
12982600990eSRalf Baechle }
12992600990eSRalf Baechle 
13002600990eSRalf Baechle EXPORT_SYMBOL(vpe_getuid);
13012600990eSRalf Baechle 
13022600990eSRalf Baechle int vpe_getgid(int index)
13032600990eSRalf Baechle {
13042600990eSRalf Baechle 	struct vpe *v;
13052600990eSRalf Baechle 
13062600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13072600990eSRalf Baechle 		return -1;
13082600990eSRalf Baechle 
13092600990eSRalf Baechle 	return v->gid;
13102600990eSRalf Baechle }
13112600990eSRalf Baechle 
13122600990eSRalf Baechle EXPORT_SYMBOL(vpe_getgid);
13132600990eSRalf Baechle 
13142600990eSRalf Baechle int vpe_notify(int index, struct vpe_notifications *notify)
13152600990eSRalf Baechle {
13162600990eSRalf Baechle 	struct vpe *v;
13172600990eSRalf Baechle 
13182600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13192600990eSRalf Baechle 		return -1;
13202600990eSRalf Baechle 
13212600990eSRalf Baechle 	list_add(&notify->list, &v->notify);
13222600990eSRalf Baechle 	return 0;
13232600990eSRalf Baechle }
13242600990eSRalf Baechle 
13252600990eSRalf Baechle EXPORT_SYMBOL(vpe_notify);
13262600990eSRalf Baechle 
13272600990eSRalf Baechle char *vpe_getcwd(int index)
13282600990eSRalf Baechle {
13292600990eSRalf Baechle 	struct vpe *v;
13302600990eSRalf Baechle 
13312600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13322600990eSRalf Baechle 		return NULL;
13332600990eSRalf Baechle 
13342600990eSRalf Baechle 	return v->cwd;
13352600990eSRalf Baechle }
13362600990eSRalf Baechle 
13372600990eSRalf Baechle EXPORT_SYMBOL(vpe_getcwd);
13382600990eSRalf Baechle 
13392600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
13402600990eSRalf Baechle static void kspd_sp_exit( int sp_id)
13412600990eSRalf Baechle {
13422600990eSRalf Baechle 	cleanup_tc(get_tc(sp_id));
13432600990eSRalf Baechle }
13442600990eSRalf Baechle #endif
13452600990eSRalf Baechle 
1346736fad17SKay Sievers static ssize_t store_kill(struct device *dev, struct device_attribute *attr,
1347736fad17SKay Sievers 			  const char *buf, size_t len)
13480f5d0df3SRalf Baechle {
13490f5d0df3SRalf Baechle 	struct vpe *vpe = get_vpe(tclimit);
13500f5d0df3SRalf Baechle 	struct vpe_notifications *not;
13510f5d0df3SRalf Baechle 
13520f5d0df3SRalf Baechle 	list_for_each_entry(not, &vpe->notify, list) {
13530f5d0df3SRalf Baechle 		not->stop(tclimit);
13540f5d0df3SRalf Baechle 	}
13550f5d0df3SRalf Baechle 
13560f5d0df3SRalf Baechle 	release_progmem(vpe->load_addr);
13570f5d0df3SRalf Baechle 	cleanup_tc(get_tc(tclimit));
13580f5d0df3SRalf Baechle 	vpe_stop(vpe);
13590f5d0df3SRalf Baechle 	vpe_free(vpe);
13600f5d0df3SRalf Baechle 
13610f5d0df3SRalf Baechle 	return len;
13620f5d0df3SRalf Baechle }
13630f5d0df3SRalf Baechle 
1364736fad17SKay Sievers static ssize_t show_ntcs(struct device *cd, struct device_attribute *attr,
1365736fad17SKay Sievers 			 char *buf)
136641790e04SRalf Baechle {
136741790e04SRalf Baechle 	struct vpe *vpe = get_vpe(tclimit);
136841790e04SRalf Baechle 
136941790e04SRalf Baechle 	return sprintf(buf, "%d\n", vpe->ntcs);
137041790e04SRalf Baechle }
137141790e04SRalf Baechle 
1372736fad17SKay Sievers static ssize_t store_ntcs(struct device *dev, struct device_attribute *attr,
1373736fad17SKay Sievers 			  const char *buf, size_t len)
137441790e04SRalf Baechle {
137541790e04SRalf Baechle 	struct vpe *vpe = get_vpe(tclimit);
137641790e04SRalf Baechle 	unsigned long new;
137741790e04SRalf Baechle 	char *endp;
137841790e04SRalf Baechle 
137941790e04SRalf Baechle 	new = simple_strtoul(buf, &endp, 0);
138041790e04SRalf Baechle 	if (endp == buf)
138141790e04SRalf Baechle 		goto out_einval;
138241790e04SRalf Baechle 
138341790e04SRalf Baechle 	if (new == 0 || new > (hw_tcs - tclimit))
138441790e04SRalf Baechle 		goto out_einval;
138541790e04SRalf Baechle 
138641790e04SRalf Baechle 	vpe->ntcs = new;
138741790e04SRalf Baechle 
138841790e04SRalf Baechle 	return len;
138941790e04SRalf Baechle 
139041790e04SRalf Baechle out_einval:
139152a7a27cSJoe Perches 	return -EINVAL;
139241790e04SRalf Baechle }
139341790e04SRalf Baechle 
1394736fad17SKay Sievers static struct device_attribute vpe_class_attributes[] = {
13950f5d0df3SRalf Baechle 	__ATTR(kill, S_IWUSR, NULL, store_kill),
139641790e04SRalf Baechle 	__ATTR(ntcs, S_IRUGO | S_IWUSR, show_ntcs, store_ntcs),
139741790e04SRalf Baechle 	{}
139841790e04SRalf Baechle };
139941790e04SRalf Baechle 
1400736fad17SKay Sievers static void vpe_device_release(struct device *cd)
140141790e04SRalf Baechle {
140241790e04SRalf Baechle 	kfree(cd);
140341790e04SRalf Baechle }
140441790e04SRalf Baechle 
140541790e04SRalf Baechle struct class vpe_class = {
140641790e04SRalf Baechle 	.name = "vpe",
140741790e04SRalf Baechle 	.owner = THIS_MODULE,
1408736fad17SKay Sievers 	.dev_release = vpe_device_release,
1409736fad17SKay Sievers 	.dev_attrs = vpe_class_attributes,
141041790e04SRalf Baechle };
141141790e04SRalf Baechle 
1412736fad17SKay Sievers struct device vpe_device;
141327a3bbafSRalf Baechle 
1414e01402b1SRalf Baechle static int __init vpe_module_init(void)
1415e01402b1SRalf Baechle {
141607cc0c9eSRalf Baechle 	unsigned int mtflags, vpflags;
141707cc0c9eSRalf Baechle 	unsigned long flags, val;
1418e01402b1SRalf Baechle 	struct vpe *v = NULL;
1419e01402b1SRalf Baechle 	struct tc *t;
142041790e04SRalf Baechle 	int tc, err;
1421e01402b1SRalf Baechle 
1422e01402b1SRalf Baechle 	if (!cpu_has_mipsmt) {
1423e01402b1SRalf Baechle 		printk("VPE loader: not a MIPS MT capable processor\n");
1424e01402b1SRalf Baechle 		return -ENODEV;
1425e01402b1SRalf Baechle 	}
1426e01402b1SRalf Baechle 
142707cc0c9eSRalf Baechle 	if (vpelimit == 0) {
142807cc0c9eSRalf Baechle 		printk(KERN_WARNING "No VPEs reserved for AP/SP, not "
142907cc0c9eSRalf Baechle 		       "initializing VPE loader.\nPass maxvpes=<n> argument as "
143007cc0c9eSRalf Baechle 		       "kernel argument\n");
143107cc0c9eSRalf Baechle 
143207cc0c9eSRalf Baechle 		return -ENODEV;
143307cc0c9eSRalf Baechle 	}
143407cc0c9eSRalf Baechle 
143507cc0c9eSRalf Baechle 	if (tclimit == 0) {
143607cc0c9eSRalf Baechle 		printk(KERN_WARNING "No TCs reserved for AP/SP, not "
143707cc0c9eSRalf Baechle 		       "initializing VPE loader.\nPass maxtcs=<n> argument as "
143807cc0c9eSRalf Baechle 		       "kernel argument\n");
143907cc0c9eSRalf Baechle 
144007cc0c9eSRalf Baechle 		return -ENODEV;
144107cc0c9eSRalf Baechle 	}
144207cc0c9eSRalf Baechle 
1443682e852eSAlexey Dobriyan 	major = register_chrdev(0, module_name, &vpe_fops);
1444682e852eSAlexey Dobriyan 	if (major < 0) {
1445e01402b1SRalf Baechle 		printk("VPE loader: unable to register character device\n");
1446307bd284SRalf Baechle 		return major;
1447e01402b1SRalf Baechle 	}
1448e01402b1SRalf Baechle 
144941790e04SRalf Baechle 	err = class_register(&vpe_class);
145041790e04SRalf Baechle 	if (err) {
145141790e04SRalf Baechle 		printk(KERN_ERR "vpe_class registration failed\n");
145227a3bbafSRalf Baechle 		goto out_chrdev;
145327a3bbafSRalf Baechle 	}
145441790e04SRalf Baechle 
1455736fad17SKay Sievers 	device_initialize(&vpe_device);
145641790e04SRalf Baechle 	vpe_device.class	= &vpe_class,
145741790e04SRalf Baechle 	vpe_device.parent	= NULL,
14581bb5beb4SKay Sievers 	dev_set_name(&vpe_device, "vpe1");
145941790e04SRalf Baechle 	vpe_device.devt = MKDEV(major, minor);
1460736fad17SKay Sievers 	err = device_add(&vpe_device);
146141790e04SRalf Baechle 	if (err) {
146241790e04SRalf Baechle 		printk(KERN_ERR "Adding vpe_device failed\n");
146341790e04SRalf Baechle 		goto out_class;
146441790e04SRalf Baechle 	}
146527a3bbafSRalf Baechle 
146607cc0c9eSRalf Baechle 	local_irq_save(flags);
146707cc0c9eSRalf Baechle 	mtflags = dmt();
146807cc0c9eSRalf Baechle 	vpflags = dvpe();
1469e01402b1SRalf Baechle 
1470e01402b1SRalf Baechle 	/* Put MVPE's into 'configuration state' */
1471340ee4b9SRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1472e01402b1SRalf Baechle 
1473e01402b1SRalf Baechle 	/* dump_mtregs(); */
1474e01402b1SRalf Baechle 
1475e01402b1SRalf Baechle 	val = read_c0_mvpconf0();
147607cc0c9eSRalf Baechle 	hw_tcs = (val & MVPCONF0_PTC) + 1;
147707cc0c9eSRalf Baechle 	hw_vpes = ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1;
147807cc0c9eSRalf Baechle 
147907cc0c9eSRalf Baechle 	for (tc = tclimit; tc < hw_tcs; tc++) {
148007cc0c9eSRalf Baechle 		/*
148107cc0c9eSRalf Baechle 		 * Must re-enable multithreading temporarily or in case we
148207cc0c9eSRalf Baechle 		 * reschedule send IPIs or similar we might hang.
148307cc0c9eSRalf Baechle 		 */
148407cc0c9eSRalf Baechle 		clear_c0_mvpcontrol(MVPCONTROL_VPC);
148507cc0c9eSRalf Baechle 		evpe(vpflags);
148607cc0c9eSRalf Baechle 		emt(mtflags);
148707cc0c9eSRalf Baechle 		local_irq_restore(flags);
148807cc0c9eSRalf Baechle 		t = alloc_tc(tc);
148907cc0c9eSRalf Baechle 		if (!t) {
149007cc0c9eSRalf Baechle 			err = -ENOMEM;
149107cc0c9eSRalf Baechle 			goto out;
149207cc0c9eSRalf Baechle 		}
149307cc0c9eSRalf Baechle 
149407cc0c9eSRalf Baechle 		local_irq_save(flags);
149507cc0c9eSRalf Baechle 		mtflags = dmt();
149607cc0c9eSRalf Baechle 		vpflags = dvpe();
149707cc0c9eSRalf Baechle 		set_c0_mvpcontrol(MVPCONTROL_VPC);
1498e01402b1SRalf Baechle 
1499e01402b1SRalf Baechle 		/* VPE's */
150007cc0c9eSRalf Baechle 		if (tc < hw_tcs) {
150107cc0c9eSRalf Baechle 			settc(tc);
1502e01402b1SRalf Baechle 
150307cc0c9eSRalf Baechle 			if ((v = alloc_vpe(tc)) == NULL) {
1504e01402b1SRalf Baechle 				printk(KERN_WARNING "VPE: unable to allocate VPE\n");
150507cc0c9eSRalf Baechle 
150607cc0c9eSRalf Baechle 				goto out_reenable;
1507e01402b1SRalf Baechle 			}
1508e01402b1SRalf Baechle 
150941790e04SRalf Baechle 			v->ntcs = hw_tcs - tclimit;
151041790e04SRalf Baechle 
15112600990eSRalf Baechle 			/* add the tc to the list of this vpe's tc's. */
15122600990eSRalf Baechle 			list_add(&t->tc, &v->tc);
1513e01402b1SRalf Baechle 
1514e01402b1SRalf Baechle 			/* deactivate all but vpe0 */
151507cc0c9eSRalf Baechle 			if (tc >= tclimit) {
1516e01402b1SRalf Baechle 				unsigned long tmp = read_vpe_c0_vpeconf0();
1517e01402b1SRalf Baechle 
1518e01402b1SRalf Baechle 				tmp &= ~VPECONF0_VPA;
1519e01402b1SRalf Baechle 
1520e01402b1SRalf Baechle 				/* master VPE */
1521e01402b1SRalf Baechle 				tmp |= VPECONF0_MVP;
1522e01402b1SRalf Baechle 				write_vpe_c0_vpeconf0(tmp);
1523e01402b1SRalf Baechle 			}
1524e01402b1SRalf Baechle 
1525e01402b1SRalf Baechle 			/* disable multi-threading with TC's */
1526e01402b1SRalf Baechle 			write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE);
1527e01402b1SRalf Baechle 
152807cc0c9eSRalf Baechle 			if (tc >= vpelimit) {
15292600990eSRalf Baechle 				/*
15302600990eSRalf Baechle 				 * Set config to be the same as vpe0,
15312600990eSRalf Baechle 				 * particularly kseg0 coherency alg
15322600990eSRalf Baechle 				 */
1533e01402b1SRalf Baechle 				write_vpe_c0_config(read_c0_config());
1534e01402b1SRalf Baechle 			}
1535e01402b1SRalf Baechle 		}
1536e01402b1SRalf Baechle 
1537e01402b1SRalf Baechle 		/* TC's */
1538e01402b1SRalf Baechle 		t->pvpe = v;	/* set the parent vpe */
1539e01402b1SRalf Baechle 
154007cc0c9eSRalf Baechle 		if (tc >= tclimit) {
1541e01402b1SRalf Baechle 			unsigned long tmp;
1542e01402b1SRalf Baechle 
154307cc0c9eSRalf Baechle 			settc(tc);
1544e01402b1SRalf Baechle 
15452600990eSRalf Baechle 			/* Any TC that is bound to VPE0 gets left as is - in case
15462600990eSRalf Baechle 			   we are running SMTC on VPE0. A TC that is bound to any
15472600990eSRalf Baechle 			   other VPE gets bound to VPE0, ideally I'd like to make
15482600990eSRalf Baechle 			   it homeless but it doesn't appear to let me bind a TC
15492600990eSRalf Baechle 			   to a non-existent VPE. Which is perfectly reasonable.
15502600990eSRalf Baechle 
15512600990eSRalf Baechle 			   The (un)bound state is visible to an EJTAG probe so may
15522600990eSRalf Baechle 			   notify GDB...
15532600990eSRalf Baechle 			*/
15542600990eSRalf Baechle 
15552600990eSRalf Baechle 			if (((tmp = read_tc_c0_tcbind()) & TCBIND_CURVPE)) {
15562600990eSRalf Baechle 				/* tc is bound >vpe0 */
15572600990eSRalf Baechle 				write_tc_c0_tcbind(tmp & ~TCBIND_CURVPE);
15582600990eSRalf Baechle 
15592600990eSRalf Baechle 				t->pvpe = get_vpe(0);	/* set the parent vpe */
15602600990eSRalf Baechle 			}
1561e01402b1SRalf Baechle 
15627c3a622dSNigel Stephens 			/* halt the TC */
15637c3a622dSNigel Stephens 			write_tc_c0_tchalt(TCHALT_H);
15647c3a622dSNigel Stephens 			mips_ihb();
15657c3a622dSNigel Stephens 
1566e01402b1SRalf Baechle 			tmp = read_tc_c0_tcstatus();
1567e01402b1SRalf Baechle 
15682600990eSRalf Baechle 			/* mark not activated and not dynamically allocatable */
1569e01402b1SRalf Baechle 			tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1570e01402b1SRalf Baechle 			tmp |= TCSTATUS_IXMT;	/* interrupt exempt */
1571e01402b1SRalf Baechle 			write_tc_c0_tcstatus(tmp);
1572e01402b1SRalf Baechle 		}
1573e01402b1SRalf Baechle 	}
1574e01402b1SRalf Baechle 
157507cc0c9eSRalf Baechle out_reenable:
1576e01402b1SRalf Baechle 	/* release config state */
1577340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
1578e01402b1SRalf Baechle 
157907cc0c9eSRalf Baechle 	evpe(vpflags);
158007cc0c9eSRalf Baechle 	emt(mtflags);
158107cc0c9eSRalf Baechle 	local_irq_restore(flags);
158207cc0c9eSRalf Baechle 
15832600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
15842600990eSRalf Baechle 	kspd_events.kspd_sp_exit = kspd_sp_exit;
15852600990eSRalf Baechle #endif
1586e01402b1SRalf Baechle 	return 0;
158727a3bbafSRalf Baechle 
158841790e04SRalf Baechle out_class:
158941790e04SRalf Baechle 	class_unregister(&vpe_class);
159027a3bbafSRalf Baechle out_chrdev:
159127a3bbafSRalf Baechle 	unregister_chrdev(major, module_name);
159227a3bbafSRalf Baechle 
159307cc0c9eSRalf Baechle out:
159427a3bbafSRalf Baechle 	return err;
1595e01402b1SRalf Baechle }
1596e01402b1SRalf Baechle 
1597e01402b1SRalf Baechle static void __exit vpe_module_exit(void)
1598e01402b1SRalf Baechle {
1599e01402b1SRalf Baechle 	struct vpe *v, *n;
1600e01402b1SRalf Baechle 
1601e01402b1SRalf Baechle 	list_for_each_entry_safe(v, n, &vpecontrol.vpe_list, list) {
1602e01402b1SRalf Baechle 		if (v->state != VPE_STATE_UNUSED) {
1603e01402b1SRalf Baechle 			release_vpe(v);
1604e01402b1SRalf Baechle 		}
1605e01402b1SRalf Baechle 	}
1606e01402b1SRalf Baechle 
1607736fad17SKay Sievers 	device_del(&vpe_device);
1608e01402b1SRalf Baechle 	unregister_chrdev(major, module_name);
1609e01402b1SRalf Baechle }
1610e01402b1SRalf Baechle 
1611e01402b1SRalf Baechle module_init(vpe_module_init);
1612e01402b1SRalf Baechle module_exit(vpe_module_exit);
1613e01402b1SRalf Baechle MODULE_DESCRIPTION("MIPS VPE Loader");
16142600990eSRalf Baechle MODULE_AUTHOR("Elizabeth Oldham, MIPS Technologies, Inc.");
1615e01402b1SRalf Baechle MODULE_LICENSE("GPL");
1616