xref: /openbmc/linux/arch/mips/kernel/vpe.c (revision 982f6ffe)
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;
77982f6ffeSRalf Baechle static int kspd_events_reqd;
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 /* allocate a vpe and associate it with this minor (or index) */
189f18b51ccSRalf Baechle static struct vpe *alloc_vpe(int minor)
190e01402b1SRalf Baechle {
191e01402b1SRalf Baechle 	struct vpe *v;
192e01402b1SRalf Baechle 
193307bd284SRalf Baechle 	if ((v = kzalloc(sizeof(struct vpe), GFP_KERNEL)) == NULL) {
194e01402b1SRalf Baechle 		return NULL;
195e01402b1SRalf Baechle 	}
196e01402b1SRalf Baechle 
197e01402b1SRalf Baechle 	INIT_LIST_HEAD(&v->tc);
198e01402b1SRalf Baechle 	list_add_tail(&v->list, &vpecontrol.vpe_list);
199e01402b1SRalf Baechle 
2002600990eSRalf Baechle 	INIT_LIST_HEAD(&v->notify);
201e01402b1SRalf Baechle 	v->minor = minor;
202e01402b1SRalf Baechle 	return v;
203e01402b1SRalf Baechle }
204e01402b1SRalf Baechle 
205e01402b1SRalf Baechle /* allocate a tc. At startup only tc0 is running, all other can be halted. */
206f18b51ccSRalf Baechle static struct tc *alloc_tc(int index)
207e01402b1SRalf Baechle {
20807cc0c9eSRalf Baechle 	struct tc *tc;
209e01402b1SRalf Baechle 
21007cc0c9eSRalf Baechle 	if ((tc = kzalloc(sizeof(struct tc), GFP_KERNEL)) == NULL)
21107cc0c9eSRalf Baechle 		goto out;
212e01402b1SRalf Baechle 
21307cc0c9eSRalf Baechle 	INIT_LIST_HEAD(&tc->tc);
21407cc0c9eSRalf Baechle 	tc->index = index;
21507cc0c9eSRalf Baechle 	list_add_tail(&tc->list, &vpecontrol.tc_list);
216e01402b1SRalf Baechle 
21707cc0c9eSRalf Baechle out:
21807cc0c9eSRalf Baechle 	return tc;
219e01402b1SRalf Baechle }
220e01402b1SRalf Baechle 
221e01402b1SRalf Baechle /* clean up and free everything */
222f18b51ccSRalf Baechle static void release_vpe(struct vpe *v)
223e01402b1SRalf Baechle {
224e01402b1SRalf Baechle 	list_del(&v->list);
225e01402b1SRalf Baechle 	if (v->load_addr)
226e01402b1SRalf Baechle 		release_progmem(v);
227e01402b1SRalf Baechle 	kfree(v);
228e01402b1SRalf Baechle }
229e01402b1SRalf Baechle 
230f18b51ccSRalf Baechle static void dump_mtregs(void)
231e01402b1SRalf Baechle {
232e01402b1SRalf Baechle 	unsigned long val;
233e01402b1SRalf Baechle 
234e01402b1SRalf Baechle 	val = read_c0_config3();
235e01402b1SRalf Baechle 	printk("config3 0x%lx MT %ld\n", val,
236e01402b1SRalf Baechle 	       (val & CONFIG3_MT) >> CONFIG3_MT_SHIFT);
237e01402b1SRalf Baechle 
238e01402b1SRalf Baechle 	val = read_c0_mvpcontrol();
239e01402b1SRalf Baechle 	printk("MVPControl 0x%lx, STLB %ld VPC %ld EVP %ld\n", val,
240e01402b1SRalf Baechle 	       (val & MVPCONTROL_STLB) >> MVPCONTROL_STLB_SHIFT,
241e01402b1SRalf Baechle 	       (val & MVPCONTROL_VPC) >> MVPCONTROL_VPC_SHIFT,
242e01402b1SRalf Baechle 	       (val & MVPCONTROL_EVP));
243e01402b1SRalf Baechle 
2442600990eSRalf Baechle 	val = read_c0_mvpconf0();
2452600990eSRalf Baechle 	printk("mvpconf0 0x%lx, PVPE %ld PTC %ld M %ld\n", val,
2462600990eSRalf Baechle 	       (val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT,
2472600990eSRalf Baechle 	       val & MVPCONF0_PTC, (val & MVPCONF0_M) >> MVPCONF0_M_SHIFT);
248e01402b1SRalf Baechle }
249e01402b1SRalf Baechle 
250e01402b1SRalf Baechle /* Find some VPE program space  */
251571e0bedSRalf Baechle static void *alloc_progmem(unsigned long len)
252e01402b1SRalf Baechle {
2535408c490SRalf Baechle 	void *addr;
2545408c490SRalf Baechle 
255e01402b1SRalf Baechle #ifdef CONFIG_MIPS_VPE_LOADER_TOM
2565408c490SRalf Baechle 	/*
2575408c490SRalf Baechle 	 * This means you must tell Linux to use less memory than you
2585408c490SRalf Baechle 	 * physically have, for example by passing a mem= boot argument.
2595408c490SRalf Baechle 	 */
2609f2546adSRalf Baechle 	addr = pfn_to_kaddr(max_low_pfn);
2615408c490SRalf Baechle 	memset(addr, 0, len);
262e01402b1SRalf Baechle #else
2635408c490SRalf Baechle 	/* simple grab some mem for now */
2645408c490SRalf Baechle 	addr = kzalloc(len, GFP_KERNEL);
265e01402b1SRalf Baechle #endif
2665408c490SRalf Baechle 
2675408c490SRalf Baechle 	return addr;
268e01402b1SRalf Baechle }
269e01402b1SRalf Baechle 
270e01402b1SRalf Baechle static void release_progmem(void *ptr)
271e01402b1SRalf Baechle {
272e01402b1SRalf Baechle #ifndef CONFIG_MIPS_VPE_LOADER_TOM
273e01402b1SRalf Baechle 	kfree(ptr);
274e01402b1SRalf Baechle #endif
275e01402b1SRalf Baechle }
276e01402b1SRalf Baechle 
277e01402b1SRalf Baechle /* Update size with this section: return offset. */
278e01402b1SRalf Baechle static long get_offset(unsigned long *size, Elf_Shdr * sechdr)
279e01402b1SRalf Baechle {
280e01402b1SRalf Baechle 	long ret;
281e01402b1SRalf Baechle 
282e01402b1SRalf Baechle 	ret = ALIGN(*size, sechdr->sh_addralign ? : 1);
283e01402b1SRalf Baechle 	*size = ret + sechdr->sh_size;
284e01402b1SRalf Baechle 	return ret;
285e01402b1SRalf Baechle }
286e01402b1SRalf Baechle 
287e01402b1SRalf Baechle /* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld
288e01402b1SRalf Baechle    might -- code, read-only data, read-write data, small data.  Tally
289e01402b1SRalf Baechle    sizes, and place the offsets into sh_entsize fields: high bit means it
290e01402b1SRalf Baechle    belongs in init. */
291e01402b1SRalf Baechle static void layout_sections(struct module *mod, const Elf_Ehdr * hdr,
292e01402b1SRalf Baechle 			    Elf_Shdr * sechdrs, const char *secstrings)
293e01402b1SRalf Baechle {
294e01402b1SRalf Baechle 	static unsigned long const masks[][2] = {
295e01402b1SRalf Baechle 		/* NOTE: all executable code must be the first section
296e01402b1SRalf Baechle 		 * in this array; otherwise modify the text_size
297e01402b1SRalf Baechle 		 * finder in the two loops below */
298e01402b1SRalf Baechle 		{SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL},
299e01402b1SRalf Baechle 		{SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL},
300e01402b1SRalf Baechle 		{SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL},
301e01402b1SRalf Baechle 		{ARCH_SHF_SMALL | SHF_ALLOC, 0}
302e01402b1SRalf Baechle 	};
303e01402b1SRalf Baechle 	unsigned int m, i;
304e01402b1SRalf Baechle 
305e01402b1SRalf Baechle 	for (i = 0; i < hdr->e_shnum; i++)
306e01402b1SRalf Baechle 		sechdrs[i].sh_entsize = ~0UL;
307e01402b1SRalf Baechle 
308e01402b1SRalf Baechle 	for (m = 0; m < ARRAY_SIZE(masks); ++m) {
309e01402b1SRalf Baechle 		for (i = 0; i < hdr->e_shnum; ++i) {
310e01402b1SRalf Baechle 			Elf_Shdr *s = &sechdrs[i];
311e01402b1SRalf Baechle 
312e01402b1SRalf Baechle 			//  || strncmp(secstrings + s->sh_name, ".init", 5) == 0)
313e01402b1SRalf Baechle 			if ((s->sh_flags & masks[m][0]) != masks[m][0]
314e01402b1SRalf Baechle 			    || (s->sh_flags & masks[m][1])
315e01402b1SRalf Baechle 			    || s->sh_entsize != ~0UL)
316e01402b1SRalf Baechle 				continue;
317e2a9cf96SRaghu Gandham 			s->sh_entsize =
318e2a9cf96SRaghu Gandham 				get_offset((unsigned long *)&mod->core_size, s);
319e01402b1SRalf Baechle 		}
320e01402b1SRalf Baechle 
321e01402b1SRalf Baechle 		if (m == 0)
322e01402b1SRalf Baechle 			mod->core_text_size = mod->core_size;
323e01402b1SRalf Baechle 
324e01402b1SRalf Baechle 	}
325e01402b1SRalf Baechle }
326e01402b1SRalf Baechle 
327e01402b1SRalf Baechle 
328e01402b1SRalf Baechle /* from module-elf32.c, but subverted a little */
329e01402b1SRalf Baechle 
330e01402b1SRalf Baechle struct mips_hi16 {
331e01402b1SRalf Baechle 	struct mips_hi16 *next;
332e01402b1SRalf Baechle 	Elf32_Addr *addr;
333e01402b1SRalf Baechle 	Elf32_Addr value;
334e01402b1SRalf Baechle };
335e01402b1SRalf Baechle 
336e01402b1SRalf Baechle static struct mips_hi16 *mips_hi16_list;
337e01402b1SRalf Baechle static unsigned int gp_offs, gp_addr;
338e01402b1SRalf Baechle 
339e01402b1SRalf Baechle static int apply_r_mips_none(struct module *me, uint32_t *location,
340e01402b1SRalf Baechle 			     Elf32_Addr v)
341e01402b1SRalf Baechle {
342e01402b1SRalf Baechle 	return 0;
343e01402b1SRalf Baechle }
344e01402b1SRalf Baechle 
345e01402b1SRalf Baechle static int apply_r_mips_gprel16(struct module *me, uint32_t *location,
346e01402b1SRalf Baechle 				Elf32_Addr v)
347e01402b1SRalf Baechle {
348e01402b1SRalf Baechle 	int rel;
349e01402b1SRalf Baechle 
350e01402b1SRalf Baechle 	if( !(*location & 0xffff) ) {
351e01402b1SRalf Baechle 		rel = (int)v - gp_addr;
352e01402b1SRalf Baechle 	}
353e01402b1SRalf Baechle 	else {
354e01402b1SRalf Baechle 		/* .sbss + gp(relative) + offset */
355e01402b1SRalf Baechle 		/* kludge! */
356e01402b1SRalf Baechle 		rel =  (int)(short)((int)v + gp_offs +
357e01402b1SRalf Baechle 				    (int)(short)(*location & 0xffff) - gp_addr);
358e01402b1SRalf Baechle 	}
359e01402b1SRalf Baechle 
360e01402b1SRalf Baechle 	if( (rel > 32768) || (rel < -32768) ) {
3612600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: apply_r_mips_gprel16: "
3622600990eSRalf Baechle 		       "relative address 0x%x out of range of gp register\n",
3632600990eSRalf Baechle 		       rel);
364e01402b1SRalf Baechle 		return -ENOEXEC;
365e01402b1SRalf Baechle 	}
366e01402b1SRalf Baechle 
367e01402b1SRalf Baechle 	*location = (*location & 0xffff0000) | (rel & 0xffff);
368e01402b1SRalf Baechle 
369e01402b1SRalf Baechle 	return 0;
370e01402b1SRalf Baechle }
371e01402b1SRalf Baechle 
372e01402b1SRalf Baechle static int apply_r_mips_pc16(struct module *me, uint32_t *location,
373e01402b1SRalf Baechle 			     Elf32_Addr v)
374e01402b1SRalf Baechle {
375e01402b1SRalf Baechle 	int rel;
376e01402b1SRalf Baechle 	rel = (((unsigned int)v - (unsigned int)location));
377e01402b1SRalf Baechle 	rel >>= 2;		// because the offset is in _instructions_ not bytes.
378e01402b1SRalf Baechle 	rel -= 1;		// and one instruction less due to the branch delay slot.
379e01402b1SRalf Baechle 
380e01402b1SRalf Baechle 	if( (rel > 32768) || (rel < -32768) ) {
3812600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: "
382e01402b1SRalf Baechle  		       "apply_r_mips_pc16: relative address out of range 0x%x\n", rel);
383e01402b1SRalf Baechle 		return -ENOEXEC;
384e01402b1SRalf Baechle 	}
385e01402b1SRalf Baechle 
386e01402b1SRalf Baechle 	*location = (*location & 0xffff0000) | (rel & 0xffff);
387e01402b1SRalf Baechle 
388e01402b1SRalf Baechle 	return 0;
389e01402b1SRalf Baechle }
390e01402b1SRalf Baechle 
391e01402b1SRalf Baechle static int apply_r_mips_32(struct module *me, uint32_t *location,
392e01402b1SRalf Baechle 			   Elf32_Addr v)
393e01402b1SRalf Baechle {
394e01402b1SRalf Baechle 	*location += v;
395e01402b1SRalf Baechle 
396e01402b1SRalf Baechle 	return 0;
397e01402b1SRalf Baechle }
398e01402b1SRalf Baechle 
399e01402b1SRalf Baechle static int apply_r_mips_26(struct module *me, uint32_t *location,
400e01402b1SRalf Baechle 			   Elf32_Addr v)
401e01402b1SRalf Baechle {
402e01402b1SRalf Baechle 	if (v % 4) {
4032600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: apply_r_mips_26 "
4042600990eSRalf Baechle 		       " unaligned relocation\n");
405e01402b1SRalf Baechle 		return -ENOEXEC;
406e01402b1SRalf Baechle 	}
407e01402b1SRalf Baechle 
408307bd284SRalf Baechle /*
409307bd284SRalf Baechle  * Not desperately convinced this is a good check of an overflow condition
410307bd284SRalf Baechle  * anyway. But it gets in the way of handling undefined weak symbols which
411307bd284SRalf Baechle  * we want to set to zero.
412307bd284SRalf Baechle  * if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) {
413307bd284SRalf Baechle  * printk(KERN_ERR
414307bd284SRalf Baechle  * "module %s: relocation overflow\n",
415307bd284SRalf Baechle  * me->name);
416307bd284SRalf Baechle  * return -ENOEXEC;
417307bd284SRalf Baechle  * }
418e01402b1SRalf Baechle  */
419e01402b1SRalf Baechle 
420e01402b1SRalf Baechle 	*location = (*location & ~0x03ffffff) |
421e01402b1SRalf Baechle 		((*location + (v >> 2)) & 0x03ffffff);
422e01402b1SRalf Baechle 	return 0;
423e01402b1SRalf Baechle }
424e01402b1SRalf Baechle 
425e01402b1SRalf Baechle static int apply_r_mips_hi16(struct module *me, uint32_t *location,
426e01402b1SRalf Baechle 			     Elf32_Addr v)
427e01402b1SRalf Baechle {
428e01402b1SRalf Baechle 	struct mips_hi16 *n;
429e01402b1SRalf Baechle 
430e01402b1SRalf Baechle 	/*
431e01402b1SRalf Baechle 	 * We cannot relocate this one now because we don't know the value of
432e01402b1SRalf Baechle 	 * the carry we need to add.  Save the information, and let LO16 do the
433e01402b1SRalf Baechle 	 * actual relocation.
434e01402b1SRalf Baechle 	 */
435e01402b1SRalf Baechle 	n = kmalloc(sizeof *n, GFP_KERNEL);
436e01402b1SRalf Baechle 	if (!n)
437e01402b1SRalf Baechle 		return -ENOMEM;
438e01402b1SRalf Baechle 
439e01402b1SRalf Baechle 	n->addr = location;
440e01402b1SRalf Baechle 	n->value = v;
441e01402b1SRalf Baechle 	n->next = mips_hi16_list;
442e01402b1SRalf Baechle 	mips_hi16_list = n;
443e01402b1SRalf Baechle 
444e01402b1SRalf Baechle 	return 0;
445e01402b1SRalf Baechle }
446e01402b1SRalf Baechle 
447e01402b1SRalf Baechle static int apply_r_mips_lo16(struct module *me, uint32_t *location,
448e01402b1SRalf Baechle 			     Elf32_Addr v)
449e01402b1SRalf Baechle {
450e01402b1SRalf Baechle 	unsigned long insnlo = *location;
451e01402b1SRalf Baechle 	Elf32_Addr val, vallo;
452477c4b07SRalf Baechle 	struct mips_hi16 *l, *next;
453e01402b1SRalf Baechle 
454e01402b1SRalf Baechle 	/* Sign extend the addend we extract from the lo insn.  */
455e01402b1SRalf Baechle 	vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;
456e01402b1SRalf Baechle 
457e01402b1SRalf Baechle 	if (mips_hi16_list != NULL) {
458e01402b1SRalf Baechle 
459e01402b1SRalf Baechle 		l = mips_hi16_list;
460e01402b1SRalf Baechle 		while (l != NULL) {
461e01402b1SRalf Baechle 			unsigned long insn;
462e01402b1SRalf Baechle 
463e01402b1SRalf Baechle 			/*
464e01402b1SRalf Baechle 			 * The value for the HI16 had best be the same.
465e01402b1SRalf Baechle 			 */
466e01402b1SRalf Baechle  			if (v != l->value) {
4672600990eSRalf Baechle 				printk(KERN_DEBUG "VPE loader: "
468b1e3afa0SJoe Perches 				       "apply_r_mips_lo16/hi16: \t"
4692600990eSRalf Baechle 				       "inconsistent value information\n");
470477c4b07SRalf Baechle 				goto out_free;
471e01402b1SRalf Baechle 			}
472e01402b1SRalf Baechle 
473e01402b1SRalf Baechle 			/*
474e01402b1SRalf Baechle 			 * Do the HI16 relocation.  Note that we actually don't
475e01402b1SRalf Baechle 			 * need to know anything about the LO16 itself, except
476e01402b1SRalf Baechle 			 * where to find the low 16 bits of the addend needed
477e01402b1SRalf Baechle 			 * by the LO16.
478e01402b1SRalf Baechle 			 */
479e01402b1SRalf Baechle 			insn = *l->addr;
480e01402b1SRalf Baechle 			val = ((insn & 0xffff) << 16) + vallo;
481e01402b1SRalf Baechle 			val += v;
482e01402b1SRalf Baechle 
483e01402b1SRalf Baechle 			/*
484e01402b1SRalf Baechle 			 * Account for the sign extension that will happen in
485e01402b1SRalf Baechle 			 * the low bits.
486e01402b1SRalf Baechle 			 */
487e01402b1SRalf Baechle 			val = ((val >> 16) + ((val & 0x8000) != 0)) & 0xffff;
488e01402b1SRalf Baechle 
489e01402b1SRalf Baechle 			insn = (insn & ~0xffff) | val;
490e01402b1SRalf Baechle 			*l->addr = insn;
491e01402b1SRalf Baechle 
492e01402b1SRalf Baechle 			next = l->next;
493e01402b1SRalf Baechle 			kfree(l);
494e01402b1SRalf Baechle 			l = next;
495e01402b1SRalf Baechle 		}
496e01402b1SRalf Baechle 
497e01402b1SRalf Baechle 		mips_hi16_list = NULL;
498e01402b1SRalf Baechle 	}
499e01402b1SRalf Baechle 
500e01402b1SRalf Baechle 	/*
501e01402b1SRalf Baechle 	 * Ok, we're done with the HI16 relocs.  Now deal with the LO16.
502e01402b1SRalf Baechle 	 */
503e01402b1SRalf Baechle 	val = v + vallo;
504e01402b1SRalf Baechle 	insnlo = (insnlo & ~0xffff) | (val & 0xffff);
505e01402b1SRalf Baechle 	*location = insnlo;
506e01402b1SRalf Baechle 
507e01402b1SRalf Baechle 	return 0;
508477c4b07SRalf Baechle 
509477c4b07SRalf Baechle out_free:
510477c4b07SRalf Baechle 	while (l != NULL) {
511477c4b07SRalf Baechle 		next = l->next;
512477c4b07SRalf Baechle 		kfree(l);
513477c4b07SRalf Baechle 		l = next;
514477c4b07SRalf Baechle 	}
515477c4b07SRalf Baechle 	mips_hi16_list = NULL;
516477c4b07SRalf Baechle 
517477c4b07SRalf Baechle 	return -ENOEXEC;
518e01402b1SRalf Baechle }
519e01402b1SRalf Baechle 
520e01402b1SRalf Baechle static int (*reloc_handlers[]) (struct module *me, uint32_t *location,
521e01402b1SRalf Baechle 				Elf32_Addr v) = {
522e01402b1SRalf Baechle 	[R_MIPS_NONE]	= apply_r_mips_none,
523e01402b1SRalf Baechle 	[R_MIPS_32]	= apply_r_mips_32,
524e01402b1SRalf Baechle 	[R_MIPS_26]	= apply_r_mips_26,
525e01402b1SRalf Baechle 	[R_MIPS_HI16]	= apply_r_mips_hi16,
526e01402b1SRalf Baechle 	[R_MIPS_LO16]	= apply_r_mips_lo16,
527e01402b1SRalf Baechle 	[R_MIPS_GPREL16] = apply_r_mips_gprel16,
528e01402b1SRalf Baechle 	[R_MIPS_PC16] = apply_r_mips_pc16
529e01402b1SRalf Baechle };
530e01402b1SRalf Baechle 
5312600990eSRalf Baechle static char *rstrs[] = {
5322600990eSRalf Baechle 	[R_MIPS_NONE]	= "MIPS_NONE",
5332600990eSRalf Baechle 	[R_MIPS_32]	= "MIPS_32",
5342600990eSRalf Baechle 	[R_MIPS_26]	= "MIPS_26",
5352600990eSRalf Baechle 	[R_MIPS_HI16]	= "MIPS_HI16",
5362600990eSRalf Baechle 	[R_MIPS_LO16]	= "MIPS_LO16",
5372600990eSRalf Baechle 	[R_MIPS_GPREL16] = "MIPS_GPREL16",
5382600990eSRalf Baechle 	[R_MIPS_PC16] = "MIPS_PC16"
5392600990eSRalf Baechle };
540e01402b1SRalf Baechle 
541f18b51ccSRalf Baechle static int apply_relocations(Elf32_Shdr *sechdrs,
542e01402b1SRalf Baechle 		      const char *strtab,
543e01402b1SRalf Baechle 		      unsigned int symindex,
544e01402b1SRalf Baechle 		      unsigned int relsec,
545e01402b1SRalf Baechle 		      struct module *me)
546e01402b1SRalf Baechle {
547e01402b1SRalf Baechle 	Elf32_Rel *rel = (void *) sechdrs[relsec].sh_addr;
548e01402b1SRalf Baechle 	Elf32_Sym *sym;
549e01402b1SRalf Baechle 	uint32_t *location;
550e01402b1SRalf Baechle 	unsigned int i;
551e01402b1SRalf Baechle 	Elf32_Addr v;
552e01402b1SRalf Baechle 	int res;
553e01402b1SRalf Baechle 
554e01402b1SRalf Baechle 	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
555e01402b1SRalf Baechle 		Elf32_Word r_info = rel[i].r_info;
556e01402b1SRalf Baechle 
557e01402b1SRalf Baechle 		/* This is where to make the change */
558e01402b1SRalf Baechle 		location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
559e01402b1SRalf Baechle 			+ rel[i].r_offset;
560e01402b1SRalf Baechle 		/* This is the symbol it is referring to */
561e01402b1SRalf Baechle 		sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
562e01402b1SRalf Baechle 			+ ELF32_R_SYM(r_info);
563e01402b1SRalf Baechle 
564e01402b1SRalf Baechle 		if (!sym->st_value) {
565e01402b1SRalf Baechle 			printk(KERN_DEBUG "%s: undefined weak symbol %s\n",
566e01402b1SRalf Baechle 			       me->name, strtab + sym->st_name);
567e01402b1SRalf Baechle 			/* just print the warning, dont barf */
568e01402b1SRalf Baechle 		}
569e01402b1SRalf Baechle 
570e01402b1SRalf Baechle 		v = sym->st_value;
571e01402b1SRalf Baechle 
572e01402b1SRalf Baechle 		res = reloc_handlers[ELF32_R_TYPE(r_info)](me, location, v);
573e01402b1SRalf Baechle 		if( res ) {
5742600990eSRalf Baechle 			char *r = rstrs[ELF32_R_TYPE(r_info)];
5752600990eSRalf Baechle 		    	printk(KERN_WARNING "VPE loader: .text+0x%x "
5762600990eSRalf Baechle 			       "relocation type %s for symbol \"%s\" failed\n",
5772600990eSRalf Baechle 			       rel[i].r_offset, r ? r : "UNKNOWN",
5782600990eSRalf Baechle 			       strtab + sym->st_name);
579e01402b1SRalf Baechle 			return res;
580e01402b1SRalf Baechle 		}
5812600990eSRalf Baechle 	}
582e01402b1SRalf Baechle 
583e01402b1SRalf Baechle 	return 0;
584e01402b1SRalf Baechle }
585e01402b1SRalf Baechle 
586f18b51ccSRalf Baechle static inline void save_gp_address(unsigned int secbase, unsigned int rel)
587e01402b1SRalf Baechle {
588e01402b1SRalf Baechle 	gp_addr = secbase + rel;
589e01402b1SRalf Baechle 	gp_offs = gp_addr - (secbase & 0xffff0000);
590e01402b1SRalf Baechle }
591e01402b1SRalf Baechle /* end module-elf32.c */
592e01402b1SRalf Baechle 
593e01402b1SRalf Baechle 
594e01402b1SRalf Baechle 
595e01402b1SRalf Baechle /* Change all symbols so that sh_value encodes the pointer directly. */
5962600990eSRalf Baechle static void simplify_symbols(Elf_Shdr * sechdrs,
597e01402b1SRalf Baechle 			    unsigned int symindex,
598e01402b1SRalf Baechle 			    const char *strtab,
599e01402b1SRalf Baechle 			    const char *secstrings,
600e01402b1SRalf Baechle 			    unsigned int nsecs, struct module *mod)
601e01402b1SRalf Baechle {
602e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
603e01402b1SRalf Baechle 	unsigned long secbase, bssbase = 0;
604e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
6052600990eSRalf Baechle 	int size;
606e01402b1SRalf Baechle 
607e01402b1SRalf Baechle 	/* find the .bss section for COMMON symbols */
608e01402b1SRalf Baechle 	for (i = 0; i < nsecs; i++) {
6092600990eSRalf Baechle 		if (strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) == 0) {
610e01402b1SRalf Baechle 			bssbase = sechdrs[i].sh_addr;
6112600990eSRalf Baechle 			break;
6122600990eSRalf Baechle 		}
613e01402b1SRalf Baechle 	}
614e01402b1SRalf Baechle 
615e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
616e01402b1SRalf Baechle 		switch (sym[i].st_shndx) {
617e01402b1SRalf Baechle 		case SHN_COMMON:
6182600990eSRalf Baechle 			/* Allocate space for the symbol in the .bss section.
6192600990eSRalf Baechle 			   st_value is currently size.
620e01402b1SRalf Baechle 			   We want it to have the address of the symbol. */
621e01402b1SRalf Baechle 
622e01402b1SRalf Baechle 			size = sym[i].st_value;
623e01402b1SRalf Baechle 			sym[i].st_value = bssbase;
624e01402b1SRalf Baechle 
625e01402b1SRalf Baechle 			bssbase += size;
626e01402b1SRalf Baechle 			break;
627e01402b1SRalf Baechle 
628e01402b1SRalf Baechle 		case SHN_ABS:
629e01402b1SRalf Baechle 			/* Don't need to do anything */
630e01402b1SRalf Baechle 			break;
631e01402b1SRalf Baechle 
632e01402b1SRalf Baechle 		case SHN_UNDEF:
633e01402b1SRalf Baechle 			/* ret = -ENOENT; */
634e01402b1SRalf Baechle 			break;
635e01402b1SRalf Baechle 
636e01402b1SRalf Baechle 		case SHN_MIPS_SCOMMON:
6372600990eSRalf Baechle 			printk(KERN_DEBUG "simplify_symbols: ignoring SHN_MIPS_SCOMMON "
6382600990eSRalf Baechle 			       "symbol <%s> st_shndx %d\n", strtab + sym[i].st_name,
6392600990eSRalf Baechle 			       sym[i].st_shndx);
640e01402b1SRalf Baechle 			// .sbss section
641e01402b1SRalf Baechle 			break;
642e01402b1SRalf Baechle 
643e01402b1SRalf Baechle 		default:
644e01402b1SRalf Baechle 			secbase = sechdrs[sym[i].st_shndx].sh_addr;
645e01402b1SRalf Baechle 
646e01402b1SRalf Baechle 			if (strncmp(strtab + sym[i].st_name, "_gp", 3) == 0) {
647e01402b1SRalf Baechle 				save_gp_address(secbase, sym[i].st_value);
648e01402b1SRalf Baechle 			}
649e01402b1SRalf Baechle 
650e01402b1SRalf Baechle 			sym[i].st_value += secbase;
651e01402b1SRalf Baechle 			break;
652e01402b1SRalf Baechle 		}
653e01402b1SRalf Baechle 	}
654e01402b1SRalf Baechle }
655e01402b1SRalf Baechle 
656e01402b1SRalf Baechle #ifdef DEBUG_ELFLOADER
657e01402b1SRalf Baechle static void dump_elfsymbols(Elf_Shdr * sechdrs, unsigned int symindex,
658e01402b1SRalf Baechle 			    const char *strtab, struct module *mod)
659e01402b1SRalf Baechle {
660e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
661e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
662e01402b1SRalf Baechle 
663e01402b1SRalf Baechle 	printk(KERN_DEBUG "dump_elfsymbols: n %d\n", n);
664e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
665e01402b1SRalf Baechle 		printk(KERN_DEBUG " i %d name <%s> 0x%x\n", i,
666e01402b1SRalf Baechle 		       strtab + sym[i].st_name, sym[i].st_value);
667e01402b1SRalf Baechle 	}
668e01402b1SRalf Baechle }
669e01402b1SRalf Baechle #endif
670e01402b1SRalf Baechle 
671e01402b1SRalf Baechle /* We are prepared so configure and start the VPE... */
672be6e1437SRalf Baechle static int vpe_run(struct vpe * v)
673e01402b1SRalf Baechle {
67407cc0c9eSRalf Baechle 	unsigned long flags, val, dmt_flag;
6752600990eSRalf Baechle 	struct vpe_notifications *n;
67607cc0c9eSRalf Baechle 	unsigned int vpeflags;
677e01402b1SRalf Baechle 	struct tc *t;
678e01402b1SRalf Baechle 
679e01402b1SRalf Baechle 	/* check we are the Master VPE */
68007cc0c9eSRalf Baechle 	local_irq_save(flags);
681e01402b1SRalf Baechle 	val = read_c0_vpeconf0();
682e01402b1SRalf Baechle 	if (!(val & VPECONF0_MVP)) {
683e01402b1SRalf Baechle 		printk(KERN_WARNING
6842600990eSRalf Baechle 		       "VPE loader: only Master VPE's are allowed to configure MT\n");
68507cc0c9eSRalf Baechle 		local_irq_restore(flags);
68607cc0c9eSRalf Baechle 
687e01402b1SRalf Baechle 		return -1;
688e01402b1SRalf Baechle 	}
689e01402b1SRalf Baechle 
69007cc0c9eSRalf Baechle 	dmt_flag = dmt();
69107cc0c9eSRalf Baechle 	vpeflags = dvpe();
692e01402b1SRalf Baechle 
693e01402b1SRalf Baechle 	if (!list_empty(&v->tc)) {
694e01402b1SRalf Baechle 		if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
69507cc0c9eSRalf Baechle 			evpe(vpeflags);
69607cc0c9eSRalf Baechle 			emt(dmt_flag);
69707cc0c9eSRalf Baechle 			local_irq_restore(flags);
69807cc0c9eSRalf Baechle 
69907cc0c9eSRalf Baechle 			printk(KERN_WARNING
70007cc0c9eSRalf Baechle 			       "VPE loader: TC %d is already in use.\n",
701e01402b1SRalf Baechle                                t->index);
702e01402b1SRalf Baechle 			return -ENOEXEC;
703e01402b1SRalf Baechle 		}
704e01402b1SRalf Baechle 	} else {
70507cc0c9eSRalf Baechle 		evpe(vpeflags);
70607cc0c9eSRalf Baechle 		emt(dmt_flag);
70707cc0c9eSRalf Baechle 		local_irq_restore(flags);
70807cc0c9eSRalf Baechle 
70907cc0c9eSRalf Baechle 		printk(KERN_WARNING
71007cc0c9eSRalf Baechle 		       "VPE loader: No TC's associated with VPE %d\n",
711e01402b1SRalf Baechle 		       v->minor);
71207cc0c9eSRalf Baechle 
713e01402b1SRalf Baechle 		return -ENOEXEC;
714e01402b1SRalf Baechle 	}
715e01402b1SRalf Baechle 
7162600990eSRalf Baechle 	/* Put MVPE's into 'configuration state' */
7172600990eSRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
718e01402b1SRalf Baechle 
7192600990eSRalf Baechle 	settc(t->index);
720e01402b1SRalf Baechle 
721e01402b1SRalf Baechle 	/* should check it is halted, and not activated */
722e01402b1SRalf Baechle 	if ((read_tc_c0_tcstatus() & TCSTATUS_A) || !(read_tc_c0_tchalt() & TCHALT_H)) {
72307cc0c9eSRalf Baechle 		evpe(vpeflags);
72407cc0c9eSRalf Baechle 		emt(dmt_flag);
72507cc0c9eSRalf Baechle 		local_irq_restore(flags);
72607cc0c9eSRalf Baechle 
72707cc0c9eSRalf Baechle 		printk(KERN_WARNING "VPE loader: TC %d is already active!\n",
728e01402b1SRalf Baechle 		       t->index);
72907cc0c9eSRalf Baechle 
730e01402b1SRalf Baechle 		return -ENOEXEC;
731e01402b1SRalf Baechle 	}
732e01402b1SRalf Baechle 
733e01402b1SRalf Baechle 	/* Write the address we want it to start running from in the TCPC register. */
734e01402b1SRalf Baechle 	write_tc_c0_tcrestart((unsigned long)v->__start);
735e01402b1SRalf Baechle 	write_tc_c0_tccontext((unsigned long)0);
73607cc0c9eSRalf Baechle 
7372600990eSRalf Baechle 	/*
7382600990eSRalf Baechle 	 * Mark the TC as activated, not interrupt exempt and not dynamically
7392600990eSRalf Baechle 	 * allocatable
7402600990eSRalf Baechle 	 */
741e01402b1SRalf Baechle 	val = read_tc_c0_tcstatus();
742e01402b1SRalf Baechle 	val = (val & ~(TCSTATUS_DA | TCSTATUS_IXMT)) | TCSTATUS_A;
743e01402b1SRalf Baechle 	write_tc_c0_tcstatus(val);
744e01402b1SRalf Baechle 
745e01402b1SRalf Baechle 	write_tc_c0_tchalt(read_tc_c0_tchalt() & ~TCHALT_H);
746e01402b1SRalf Baechle 
747e01402b1SRalf Baechle 	/*
748e01402b1SRalf Baechle 	 * The sde-kit passes 'memsize' to __start in $a3, so set something
7492600990eSRalf Baechle 	 * here...  Or set $a3 to zero and define DFLT_STACK_SIZE and
750e01402b1SRalf Baechle 	 * DFLT_HEAP_SIZE when you compile your program
751e01402b1SRalf Baechle 	 */
75241790e04SRalf Baechle 	mttgpr(6, v->ntcs);
7532600990eSRalf Baechle 	mttgpr(7, physical_memsize);
754e01402b1SRalf Baechle 
7552600990eSRalf Baechle 	/* set up VPE1 */
7562600990eSRalf Baechle 	/*
7572600990eSRalf Baechle 	 * bind the TC to VPE 1 as late as possible so we only have the final
7582600990eSRalf Baechle 	 * VPE registers to set up, and so an EJTAG probe can trigger on it
7592600990eSRalf Baechle 	 */
76007cc0c9eSRalf Baechle 	write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | 1);
7612600990eSRalf Baechle 
762a94d7020SElizabeth Oldham 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~(VPECONF0_VPA));
763a94d7020SElizabeth Oldham 
764a94d7020SElizabeth Oldham 	back_to_back_c0_hazard();
765a94d7020SElizabeth Oldham 
7662600990eSRalf Baechle 	/* Set up the XTC bit in vpeconf0 to point at our tc */
7672600990eSRalf Baechle 	write_vpe_c0_vpeconf0( (read_vpe_c0_vpeconf0() & ~(VPECONF0_XTC))
7682600990eSRalf Baechle 	                      | (t->index << VPECONF0_XTC_SHIFT));
7692600990eSRalf Baechle 
770a94d7020SElizabeth Oldham 	back_to_back_c0_hazard();
771a94d7020SElizabeth Oldham 
7722600990eSRalf Baechle 	/* enable this VPE */
7732600990eSRalf Baechle 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA);
774e01402b1SRalf Baechle 
775e01402b1SRalf Baechle 	/* clear out any left overs from a previous program */
7762600990eSRalf Baechle 	write_vpe_c0_status(0);
777e01402b1SRalf Baechle 	write_vpe_c0_cause(0);
778e01402b1SRalf Baechle 
779e01402b1SRalf Baechle 	/* take system out of configuration state */
780340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
781e01402b1SRalf Baechle 
782b618336aSKevin D. Kissell 	/*
783b618336aSKevin D. Kissell 	 * SMTC/SMVP kernels manage VPE enable independently,
784b618336aSKevin D. Kissell 	 * but uniprocessor kernels need to turn it on, even
785b618336aSKevin D. Kissell 	 * if that wasn't the pre-dvpe() state.
786b618336aSKevin D. Kissell 	 */
78707cc0c9eSRalf Baechle #ifdef CONFIG_SMP
78807cc0c9eSRalf Baechle 	evpe(vpeflags);
789b618336aSKevin D. Kissell #else
790b618336aSKevin D. Kissell 	evpe(EVPE_ENABLE);
79107cc0c9eSRalf Baechle #endif
79207cc0c9eSRalf Baechle 	emt(dmt_flag);
79307cc0c9eSRalf Baechle 	local_irq_restore(flags);
794e01402b1SRalf Baechle 
79507cc0c9eSRalf Baechle 	list_for_each_entry(n, &v->notify, list)
79607cc0c9eSRalf Baechle 		n->start(minor);
7972600990eSRalf Baechle 
798e01402b1SRalf Baechle 	return 0;
799e01402b1SRalf Baechle }
800e01402b1SRalf Baechle 
8012600990eSRalf Baechle static int find_vpe_symbols(struct vpe * v, Elf_Shdr * sechdrs,
802e01402b1SRalf Baechle 				      unsigned int symindex, const char *strtab,
803e01402b1SRalf Baechle 				      struct module *mod)
804e01402b1SRalf Baechle {
805e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
806e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
807e01402b1SRalf Baechle 
808e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
809e01402b1SRalf Baechle 		if (strcmp(strtab + sym[i].st_name, "__start") == 0) {
810e01402b1SRalf Baechle 			v->__start = sym[i].st_value;
811e01402b1SRalf Baechle 		}
812e01402b1SRalf Baechle 
813e01402b1SRalf Baechle 		if (strcmp(strtab + sym[i].st_name, "vpe_shared") == 0) {
814e01402b1SRalf Baechle 			v->shared_ptr = (void *)sym[i].st_value;
815e01402b1SRalf Baechle 		}
816e01402b1SRalf Baechle 	}
817e01402b1SRalf Baechle 
8182600990eSRalf Baechle 	if ( (v->__start == 0) || (v->shared_ptr == NULL))
8192600990eSRalf Baechle 		return -1;
8202600990eSRalf Baechle 
821e01402b1SRalf Baechle 	return 0;
822e01402b1SRalf Baechle }
823e01402b1SRalf Baechle 
824307bd284SRalf Baechle /*
8252600990eSRalf Baechle  * Allocates a VPE with some program code space(the load address), copies the
8262600990eSRalf Baechle  * contents of the program (p)buffer performing relocatations/etc, free's it
8272600990eSRalf Baechle  * when finished.
828e01402b1SRalf Baechle  */
829be6e1437SRalf Baechle static int vpe_elfload(struct vpe * v)
830e01402b1SRalf Baechle {
831e01402b1SRalf Baechle 	Elf_Ehdr *hdr;
832e01402b1SRalf Baechle 	Elf_Shdr *sechdrs;
833e01402b1SRalf Baechle 	long err = 0;
834e01402b1SRalf Baechle 	char *secstrings, *strtab = NULL;
8352600990eSRalf Baechle 	unsigned int len, i, symindex = 0, strindex = 0, relocate = 0;
836e01402b1SRalf Baechle 	struct module mod;	// so we can re-use the relocations code
837e01402b1SRalf Baechle 
838e01402b1SRalf Baechle 	memset(&mod, 0, sizeof(struct module));
8392600990eSRalf Baechle 	strcpy(mod.name, "VPE loader");
840e01402b1SRalf Baechle 
841e01402b1SRalf Baechle 	hdr = (Elf_Ehdr *) v->pbuffer;
842e01402b1SRalf Baechle 	len = v->plen;
843e01402b1SRalf Baechle 
844e01402b1SRalf Baechle 	/* Sanity checks against insmoding binaries or wrong arch,
845e01402b1SRalf Baechle 	   weird elf version */
846d303f4a1SCyrill Gorcunov 	if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0
8472600990eSRalf Baechle 	    || (hdr->e_type != ET_REL && hdr->e_type != ET_EXEC)
8482600990eSRalf Baechle 	    || !elf_check_arch(hdr)
849e01402b1SRalf Baechle 	    || hdr->e_shentsize != sizeof(*sechdrs)) {
850e01402b1SRalf Baechle 		printk(KERN_WARNING
8512600990eSRalf Baechle 		       "VPE loader: program wrong arch or weird elf version\n");
852e01402b1SRalf Baechle 
853e01402b1SRalf Baechle 		return -ENOEXEC;
854e01402b1SRalf Baechle 	}
855e01402b1SRalf Baechle 
8562600990eSRalf Baechle 	if (hdr->e_type == ET_REL)
8572600990eSRalf Baechle 		relocate = 1;
8582600990eSRalf Baechle 
859e01402b1SRalf Baechle 	if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) {
8602600990eSRalf Baechle 		printk(KERN_ERR "VPE loader: program length %u truncated\n",
8612600990eSRalf Baechle 		       len);
8622600990eSRalf Baechle 
863e01402b1SRalf Baechle 		return -ENOEXEC;
864e01402b1SRalf Baechle 	}
865e01402b1SRalf Baechle 
866e01402b1SRalf Baechle 	/* Convenience variables */
867e01402b1SRalf Baechle 	sechdrs = (void *)hdr + hdr->e_shoff;
868e01402b1SRalf Baechle 	secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
869e01402b1SRalf Baechle 	sechdrs[0].sh_addr = 0;
870e01402b1SRalf Baechle 
871e01402b1SRalf Baechle 	/* And these should exist, but gcc whinges if we don't init them */
872e01402b1SRalf Baechle 	symindex = strindex = 0;
873e01402b1SRalf Baechle 
8742600990eSRalf Baechle 	if (relocate) {
875e01402b1SRalf Baechle 		for (i = 1; i < hdr->e_shnum; i++) {
876e01402b1SRalf Baechle 			if (sechdrs[i].sh_type != SHT_NOBITS
877e01402b1SRalf Baechle 			    && len < sechdrs[i].sh_offset + sechdrs[i].sh_size) {
878e01402b1SRalf Baechle 				printk(KERN_ERR "VPE program length %u truncated\n",
879e01402b1SRalf Baechle 				       len);
880e01402b1SRalf Baechle 				return -ENOEXEC;
881e01402b1SRalf Baechle 			}
882e01402b1SRalf Baechle 
883e01402b1SRalf Baechle 			/* Mark all sections sh_addr with their address in the
884e01402b1SRalf Baechle 			   temporary image. */
885e01402b1SRalf Baechle 			sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
886e01402b1SRalf Baechle 
887e01402b1SRalf Baechle 			/* Internal symbols and strings. */
888e01402b1SRalf Baechle 			if (sechdrs[i].sh_type == SHT_SYMTAB) {
889e01402b1SRalf Baechle 				symindex = i;
890e01402b1SRalf Baechle 				strindex = sechdrs[i].sh_link;
891e01402b1SRalf Baechle 				strtab = (char *)hdr + sechdrs[strindex].sh_offset;
892e01402b1SRalf Baechle 			}
893e01402b1SRalf Baechle 		}
894e01402b1SRalf Baechle 		layout_sections(&mod, hdr, sechdrs, secstrings);
8952600990eSRalf Baechle 	}
896e01402b1SRalf Baechle 
897e01402b1SRalf Baechle 	v->load_addr = alloc_progmem(mod.core_size);
8985408c490SRalf Baechle 	if (!v->load_addr)
8995408c490SRalf Baechle 		return -ENOMEM;
900e01402b1SRalf Baechle 
9015408c490SRalf Baechle 	pr_info("VPE loader: loading to %p\n", v->load_addr);
902e01402b1SRalf Baechle 
9032600990eSRalf Baechle 	if (relocate) {
904e01402b1SRalf Baechle 		for (i = 0; i < hdr->e_shnum; i++) {
905e01402b1SRalf Baechle 			void *dest;
906e01402b1SRalf Baechle 
907e01402b1SRalf Baechle 			if (!(sechdrs[i].sh_flags & SHF_ALLOC))
908e01402b1SRalf Baechle 				continue;
909e01402b1SRalf Baechle 
910e01402b1SRalf Baechle 			dest = v->load_addr + sechdrs[i].sh_entsize;
911e01402b1SRalf Baechle 
912e01402b1SRalf Baechle 			if (sechdrs[i].sh_type != SHT_NOBITS)
913e01402b1SRalf Baechle 				memcpy(dest, (void *)sechdrs[i].sh_addr,
914e01402b1SRalf Baechle 				       sechdrs[i].sh_size);
915e01402b1SRalf Baechle 			/* Update sh_addr to point to copy in image. */
916e01402b1SRalf Baechle 			sechdrs[i].sh_addr = (unsigned long)dest;
9172600990eSRalf Baechle 
9182600990eSRalf Baechle 			printk(KERN_DEBUG " section sh_name %s sh_addr 0x%x\n",
9192600990eSRalf Baechle 			       secstrings + sechdrs[i].sh_name, sechdrs[i].sh_addr);
920e01402b1SRalf Baechle 		}
921e01402b1SRalf Baechle 
922e01402b1SRalf Baechle  		/* Fix up syms, so that st_value is a pointer to location. */
923e01402b1SRalf Baechle  		simplify_symbols(sechdrs, symindex, strtab, secstrings,
924e01402b1SRalf Baechle  				 hdr->e_shnum, &mod);
925e01402b1SRalf Baechle 
926e01402b1SRalf Baechle  		/* Now do relocations. */
927e01402b1SRalf Baechle  		for (i = 1; i < hdr->e_shnum; i++) {
928e01402b1SRalf Baechle  			const char *strtab = (char *)sechdrs[strindex].sh_addr;
929e01402b1SRalf Baechle  			unsigned int info = sechdrs[i].sh_info;
930e01402b1SRalf Baechle 
931e01402b1SRalf Baechle  			/* Not a valid relocation section? */
932e01402b1SRalf Baechle  			if (info >= hdr->e_shnum)
933e01402b1SRalf Baechle  				continue;
934e01402b1SRalf Baechle 
935e01402b1SRalf Baechle  			/* Don't bother with non-allocated sections */
936e01402b1SRalf Baechle  			if (!(sechdrs[info].sh_flags & SHF_ALLOC))
937e01402b1SRalf Baechle  				continue;
938e01402b1SRalf Baechle 
939e01402b1SRalf Baechle  			if (sechdrs[i].sh_type == SHT_REL)
9402600990eSRalf Baechle  				err = apply_relocations(sechdrs, strtab, symindex, i,
9412600990eSRalf Baechle  							&mod);
942e01402b1SRalf Baechle  			else if (sechdrs[i].sh_type == SHT_RELA)
943e01402b1SRalf Baechle  				err = apply_relocate_add(sechdrs, strtab, symindex, i,
944e01402b1SRalf Baechle  							 &mod);
9452600990eSRalf Baechle  			if (err < 0)
9462600990eSRalf Baechle  				return err;
9472600990eSRalf Baechle 
9482600990eSRalf Baechle   		}
9492600990eSRalf Baechle   	} else {
950bdf5d42cSRalf Baechle 		struct elf_phdr *phdr = (struct elf_phdr *) ((char *)hdr + hdr->e_phoff);
9512600990eSRalf Baechle 
952bdf5d42cSRalf Baechle 		for (i = 0; i < hdr->e_phnum; i++) {
953b618336aSKevin D. Kissell 			if (phdr->p_type == PT_LOAD) {
954b618336aSKevin D. Kissell 				memcpy((void *)phdr->p_paddr,
955b618336aSKevin D. Kissell 				       (char *)hdr + phdr->p_offset,
956b618336aSKevin D. Kissell 				       phdr->p_filesz);
957b618336aSKevin D. Kissell 				memset((void *)phdr->p_paddr + phdr->p_filesz,
958b618336aSKevin D. Kissell 				       0, phdr->p_memsz - phdr->p_filesz);
959b618336aSKevin D. Kissell 		    }
960bdf5d42cSRalf Baechle 		    phdr++;
961bdf5d42cSRalf Baechle 		}
962bdf5d42cSRalf Baechle 
963bdf5d42cSRalf Baechle 		for (i = 0; i < hdr->e_shnum; i++) {
9642600990eSRalf Baechle  			/* Internal symbols and strings. */
9652600990eSRalf Baechle  			if (sechdrs[i].sh_type == SHT_SYMTAB) {
9662600990eSRalf Baechle  				symindex = i;
9672600990eSRalf Baechle  				strindex = sechdrs[i].sh_link;
9682600990eSRalf Baechle  				strtab = (char *)hdr + sechdrs[strindex].sh_offset;
9692600990eSRalf Baechle 
9702600990eSRalf Baechle  				/* mark the symtab's address for when we try to find the
9712600990eSRalf Baechle  				   magic symbols */
9722600990eSRalf Baechle  				sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
9732600990eSRalf Baechle  			}
974e01402b1SRalf Baechle 		}
975e01402b1SRalf Baechle 	}
976e01402b1SRalf Baechle 
977e01402b1SRalf Baechle 	/* make sure it's physically written out */
978e01402b1SRalf Baechle 	flush_icache_range((unsigned long)v->load_addr,
979e01402b1SRalf Baechle 			   (unsigned long)v->load_addr + v->len);
980e01402b1SRalf Baechle 
981e01402b1SRalf Baechle 	if ((find_vpe_symbols(v, sechdrs, symindex, strtab, &mod)) < 0) {
9822600990eSRalf Baechle 		if (v->__start == 0) {
9832600990eSRalf Baechle 			printk(KERN_WARNING "VPE loader: program does not contain "
9842600990eSRalf Baechle 			       "a __start symbol\n");
9852600990eSRalf Baechle 			return -ENOEXEC;
9862600990eSRalf Baechle 		}
987e01402b1SRalf Baechle 
9882600990eSRalf Baechle 		if (v->shared_ptr == NULL)
9892600990eSRalf Baechle 			printk(KERN_WARNING "VPE loader: "
9902600990eSRalf Baechle 			       "program does not contain vpe_shared symbol.\n"
9912600990eSRalf Baechle 			       " Unable to use AMVP (AP/SP) facilities.\n");
992e01402b1SRalf Baechle 	}
993e01402b1SRalf Baechle 
994e01402b1SRalf Baechle 	printk(" elf loaded\n");
9952600990eSRalf Baechle 	return 0;
996e01402b1SRalf Baechle }
997e01402b1SRalf Baechle 
9982600990eSRalf Baechle static void cleanup_tc(struct tc *tc)
999e01402b1SRalf Baechle {
100007cc0c9eSRalf Baechle 	unsigned long flags;
100107cc0c9eSRalf Baechle 	unsigned int mtflags, vpflags;
10022600990eSRalf Baechle 	int tmp;
1003e01402b1SRalf Baechle 
100407cc0c9eSRalf Baechle 	local_irq_save(flags);
100507cc0c9eSRalf Baechle 	mtflags = dmt();
100607cc0c9eSRalf Baechle 	vpflags = dvpe();
10072600990eSRalf Baechle 	/* Put MVPE's into 'configuration state' */
10082600990eSRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1009e01402b1SRalf Baechle 
10102600990eSRalf Baechle 	settc(tc->index);
1011e01402b1SRalf Baechle 	tmp = read_tc_c0_tcstatus();
1012e01402b1SRalf Baechle 
1013e01402b1SRalf Baechle 	/* mark not allocated and not dynamically allocatable */
1014e01402b1SRalf Baechle 	tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1015e01402b1SRalf Baechle 	tmp |= TCSTATUS_IXMT;	/* interrupt exempt */
1016e01402b1SRalf Baechle 	write_tc_c0_tcstatus(tmp);
1017e01402b1SRalf Baechle 
1018e01402b1SRalf Baechle 	write_tc_c0_tchalt(TCHALT_H);
10197c3a622dSNigel Stephens 	mips_ihb();
1020e01402b1SRalf Baechle 
10212600990eSRalf Baechle 	/* bind it to anything other than VPE1 */
102207cc0c9eSRalf Baechle //	write_tc_c0_tcbind(read_tc_c0_tcbind() & ~TCBIND_CURVPE); // | TCBIND_CURVPE
10232600990eSRalf Baechle 
10242600990eSRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
102507cc0c9eSRalf Baechle 	evpe(vpflags);
102607cc0c9eSRalf Baechle 	emt(mtflags);
102707cc0c9eSRalf Baechle 	local_irq_restore(flags);
10282600990eSRalf Baechle }
10292600990eSRalf Baechle 
10302600990eSRalf Baechle static int getcwd(char *buff, int size)
10312600990eSRalf Baechle {
10322600990eSRalf Baechle 	mm_segment_t old_fs;
10332600990eSRalf Baechle 	int ret;
10342600990eSRalf Baechle 
10352600990eSRalf Baechle 	old_fs = get_fs();
10362600990eSRalf Baechle 	set_fs(KERNEL_DS);
10372600990eSRalf Baechle 
10382600990eSRalf Baechle 	ret = sys_getcwd(buff, size);
10392600990eSRalf Baechle 
10402600990eSRalf Baechle 	set_fs(old_fs);
10412600990eSRalf Baechle 
10422600990eSRalf Baechle 	return ret;
10432600990eSRalf Baechle }
10442600990eSRalf Baechle 
10452600990eSRalf Baechle /* checks VPE is unused and gets ready to load program  */
10462600990eSRalf Baechle static int vpe_open(struct inode *inode, struct file *filp)
10472600990eSRalf Baechle {
1048c4c4018bSRalf Baechle 	enum vpe_state state;
10492600990eSRalf Baechle 	struct vpe_notifications *not;
105007cc0c9eSRalf Baechle 	struct vpe *v;
10517558da94SJonathan Corbet 	int ret, err = 0;
10522600990eSRalf Baechle 
10537558da94SJonathan Corbet 	lock_kernel();
105407cc0c9eSRalf Baechle 	if (minor != iminor(inode)) {
105507cc0c9eSRalf Baechle 		/* assume only 1 device at the moment. */
10562600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: only vpe1 is supported\n");
10577558da94SJonathan Corbet 		err = -ENODEV;
10587558da94SJonathan Corbet 		goto out;
10592600990eSRalf Baechle 	}
10602600990eSRalf Baechle 
106107cc0c9eSRalf Baechle 	if ((v = get_vpe(tclimit)) == NULL) {
10622600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: unable to get vpe\n");
10637558da94SJonathan Corbet 		err = -ENODEV;
10647558da94SJonathan Corbet 		goto out;
10652600990eSRalf Baechle 	}
10662600990eSRalf Baechle 
1067c4c4018bSRalf Baechle 	state = xchg(&v->state, VPE_STATE_INUSE);
1068c4c4018bSRalf Baechle 	if (state != VPE_STATE_UNUSED) {
10692600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: tc in use dumping regs\n");
10702600990eSRalf Baechle 
10712600990eSRalf Baechle 		list_for_each_entry(not, &v->notify, list) {
107207cc0c9eSRalf Baechle 			not->stop(tclimit);
10732600990eSRalf Baechle 		}
10742600990eSRalf Baechle 
10752600990eSRalf Baechle 		release_progmem(v->load_addr);
107607cc0c9eSRalf Baechle 		cleanup_tc(get_tc(tclimit));
1077e01402b1SRalf Baechle 	}
1078e01402b1SRalf Baechle 
1079e01402b1SRalf Baechle 	/* this of-course trashes what was there before... */
1080e01402b1SRalf Baechle 	v->pbuffer = vmalloc(P_SIZE);
1081e01402b1SRalf Baechle 	v->plen = P_SIZE;
1082e01402b1SRalf Baechle 	v->load_addr = NULL;
1083e01402b1SRalf Baechle 	v->len = 0;
1084e01402b1SRalf Baechle 
1085d76b0d9bSDavid Howells 	v->uid = filp->f_cred->fsuid;
1086d76b0d9bSDavid Howells 	v->gid = filp->f_cred->fsgid;
10872600990eSRalf Baechle 
10882600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
10892600990eSRalf Baechle 	/* get kspd to tell us when a syscall_exit happens */
10902600990eSRalf Baechle 	if (!kspd_events_reqd) {
10912600990eSRalf Baechle 		kspd_notify(&kspd_events);
10922600990eSRalf Baechle 		kspd_events_reqd++;
10932600990eSRalf Baechle 	}
10942600990eSRalf Baechle #endif
10952600990eSRalf Baechle 
10962600990eSRalf Baechle 	v->cwd[0] = 0;
10972600990eSRalf Baechle 	ret = getcwd(v->cwd, VPE_PATH_MAX);
10982600990eSRalf Baechle 	if (ret < 0)
10992600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: open, getcwd returned %d\n", ret);
11002600990eSRalf Baechle 
11012600990eSRalf Baechle 	v->shared_ptr = NULL;
11022600990eSRalf Baechle 	v->__start = 0;
110307cc0c9eSRalf Baechle 
11047558da94SJonathan Corbet out:
11057558da94SJonathan Corbet 	unlock_kernel();
1106e01402b1SRalf Baechle 	return 0;
1107e01402b1SRalf Baechle }
1108e01402b1SRalf Baechle 
1109e01402b1SRalf Baechle static int vpe_release(struct inode *inode, struct file *filp)
1110e01402b1SRalf Baechle {
1111307bd284SRalf Baechle 	struct vpe *v;
1112e01402b1SRalf Baechle 	Elf_Ehdr *hdr;
111307cc0c9eSRalf Baechle 	int ret = 0;
1114e01402b1SRalf Baechle 
111507cc0c9eSRalf Baechle 	v = get_vpe(tclimit);
111607cc0c9eSRalf Baechle 	if (v == NULL)
1117e01402b1SRalf Baechle 		return -ENODEV;
1118e01402b1SRalf Baechle 
1119e01402b1SRalf Baechle 	hdr = (Elf_Ehdr *) v->pbuffer;
1120d303f4a1SCyrill Gorcunov 	if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) == 0) {
112107cc0c9eSRalf Baechle 		if (vpe_elfload(v) >= 0) {
1122e01402b1SRalf Baechle 			vpe_run(v);
112307cc0c9eSRalf Baechle 		} else {
11242600990eSRalf Baechle  			printk(KERN_WARNING "VPE loader: ELF load failed.\n");
1125e01402b1SRalf Baechle 			ret = -ENOEXEC;
1126e01402b1SRalf Baechle 		}
1127e01402b1SRalf Baechle 	} else {
11282600990eSRalf Baechle  		printk(KERN_WARNING "VPE loader: only elf files are supported\n");
1129e01402b1SRalf Baechle 		ret = -ENOEXEC;
1130e01402b1SRalf Baechle 	}
1131e01402b1SRalf Baechle 
11322600990eSRalf Baechle 	/* It's good to be able to run the SP and if it chokes have a look at
11332600990eSRalf Baechle 	   the /dev/rt?. But if we reset the pointer to the shared struct we
11348ebcfc8bSNick Andrew 	   lose what has happened. So perhaps if garbage is sent to the vpe
11352600990eSRalf Baechle 	   device, use it as a trigger for the reset. Hopefully a nice
11362600990eSRalf Baechle 	   executable will be along shortly. */
11372600990eSRalf Baechle 	if (ret < 0)
11382600990eSRalf Baechle 		v->shared_ptr = NULL;
11392600990eSRalf Baechle 
1140e01402b1SRalf Baechle 	// cleanup any temp buffers
1141e01402b1SRalf Baechle 	if (v->pbuffer)
1142e01402b1SRalf Baechle 		vfree(v->pbuffer);
1143e01402b1SRalf Baechle 	v->plen = 0;
1144e01402b1SRalf Baechle 	return ret;
1145e01402b1SRalf Baechle }
1146e01402b1SRalf Baechle 
1147e01402b1SRalf Baechle static ssize_t vpe_write(struct file *file, const char __user * buffer,
1148e01402b1SRalf Baechle 			 size_t count, loff_t * ppos)
1149e01402b1SRalf Baechle {
1150e01402b1SRalf Baechle 	size_t ret = count;
1151307bd284SRalf Baechle 	struct vpe *v;
1152e01402b1SRalf Baechle 
115307cc0c9eSRalf Baechle 	if (iminor(file->f_path.dentry->d_inode) != minor)
115407cc0c9eSRalf Baechle 		return -ENODEV;
115507cc0c9eSRalf Baechle 
115607cc0c9eSRalf Baechle 	v = get_vpe(tclimit);
115707cc0c9eSRalf Baechle 	if (v == NULL)
1158e01402b1SRalf Baechle 		return -ENODEV;
1159e01402b1SRalf Baechle 
1160e01402b1SRalf Baechle 	if (v->pbuffer == NULL) {
11612600990eSRalf Baechle 		printk(KERN_ERR "VPE loader: no buffer for program\n");
1162e01402b1SRalf Baechle 		return -ENOMEM;
1163e01402b1SRalf Baechle 	}
1164e01402b1SRalf Baechle 
1165e01402b1SRalf Baechle 	if ((count + v->len) > v->plen) {
1166e01402b1SRalf Baechle 		printk(KERN_WARNING
11672600990eSRalf Baechle 		       "VPE loader: elf size too big. Perhaps strip uneeded symbols\n");
1168e01402b1SRalf Baechle 		return -ENOMEM;
1169e01402b1SRalf Baechle 	}
1170e01402b1SRalf Baechle 
1171e01402b1SRalf Baechle 	count -= copy_from_user(v->pbuffer + v->len, buffer, count);
11722600990eSRalf Baechle 	if (!count)
1173e01402b1SRalf Baechle 		return -EFAULT;
1174e01402b1SRalf Baechle 
1175e01402b1SRalf Baechle 	v->len += count;
1176e01402b1SRalf Baechle 	return ret;
1177e01402b1SRalf Baechle }
1178e01402b1SRalf Baechle 
11795dfe4c96SArjan van de Ven static const struct file_operations vpe_fops = {
1180e01402b1SRalf Baechle 	.owner = THIS_MODULE,
1181e01402b1SRalf Baechle 	.open = vpe_open,
1182e01402b1SRalf Baechle 	.release = vpe_release,
1183e01402b1SRalf Baechle 	.write = vpe_write
1184e01402b1SRalf Baechle };
1185e01402b1SRalf Baechle 
1186e01402b1SRalf Baechle /* module wrapper entry points */
1187e01402b1SRalf Baechle /* give me a vpe */
1188e01402b1SRalf Baechle vpe_handle vpe_alloc(void)
1189e01402b1SRalf Baechle {
1190e01402b1SRalf Baechle 	int i;
1191e01402b1SRalf Baechle 	struct vpe *v;
1192e01402b1SRalf Baechle 
1193e01402b1SRalf Baechle 	/* find a vpe */
1194e01402b1SRalf Baechle 	for (i = 1; i < MAX_VPES; i++) {
1195e01402b1SRalf Baechle 		if ((v = get_vpe(i)) != NULL) {
1196e01402b1SRalf Baechle 			v->state = VPE_STATE_INUSE;
1197e01402b1SRalf Baechle 			return v;
1198e01402b1SRalf Baechle 		}
1199e01402b1SRalf Baechle 	}
1200e01402b1SRalf Baechle 	return NULL;
1201e01402b1SRalf Baechle }
1202e01402b1SRalf Baechle 
1203e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_alloc);
1204e01402b1SRalf Baechle 
1205e01402b1SRalf Baechle /* start running from here */
1206e01402b1SRalf Baechle int vpe_start(vpe_handle vpe, unsigned long start)
1207e01402b1SRalf Baechle {
1208e01402b1SRalf Baechle 	struct vpe *v = vpe;
1209e01402b1SRalf Baechle 
1210e01402b1SRalf Baechle 	v->__start = start;
1211e01402b1SRalf Baechle 	return vpe_run(v);
1212e01402b1SRalf Baechle }
1213e01402b1SRalf Baechle 
1214e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_start);
1215e01402b1SRalf Baechle 
1216e01402b1SRalf Baechle /* halt it for now */
1217e01402b1SRalf Baechle int vpe_stop(vpe_handle vpe)
1218e01402b1SRalf Baechle {
1219e01402b1SRalf Baechle 	struct vpe *v = vpe;
1220e01402b1SRalf Baechle 	struct tc *t;
1221e01402b1SRalf Baechle 	unsigned int evpe_flags;
1222e01402b1SRalf Baechle 
1223e01402b1SRalf Baechle 	evpe_flags = dvpe();
1224e01402b1SRalf Baechle 
1225e01402b1SRalf Baechle 	if ((t = list_entry(v->tc.next, struct tc, tc)) != NULL) {
1226e01402b1SRalf Baechle 
1227e01402b1SRalf Baechle 		settc(t->index);
1228e01402b1SRalf Baechle 		write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
1229e01402b1SRalf Baechle 	}
1230e01402b1SRalf Baechle 
1231e01402b1SRalf Baechle 	evpe(evpe_flags);
1232e01402b1SRalf Baechle 
1233e01402b1SRalf Baechle 	return 0;
1234e01402b1SRalf Baechle }
1235e01402b1SRalf Baechle 
1236e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_stop);
1237e01402b1SRalf Baechle 
1238e01402b1SRalf Baechle /* I've done with it thank you */
1239e01402b1SRalf Baechle int vpe_free(vpe_handle vpe)
1240e01402b1SRalf Baechle {
1241e01402b1SRalf Baechle 	struct vpe *v = vpe;
1242e01402b1SRalf Baechle 	struct tc *t;
1243e01402b1SRalf Baechle 	unsigned int evpe_flags;
1244e01402b1SRalf Baechle 
1245e01402b1SRalf Baechle 	if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
1246e01402b1SRalf Baechle 		return -ENOEXEC;
1247e01402b1SRalf Baechle 	}
1248e01402b1SRalf Baechle 
1249e01402b1SRalf Baechle 	evpe_flags = dvpe();
1250e01402b1SRalf Baechle 
1251e01402b1SRalf Baechle 	/* Put MVPE's into 'configuration state' */
1252340ee4b9SRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1253e01402b1SRalf Baechle 
1254e01402b1SRalf Baechle 	settc(t->index);
1255e01402b1SRalf Baechle 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
1256e01402b1SRalf Baechle 
12577c3a622dSNigel Stephens 	/* halt the TC */
1258e01402b1SRalf Baechle 	write_tc_c0_tchalt(TCHALT_H);
12597c3a622dSNigel Stephens 	mips_ihb();
12607c3a622dSNigel Stephens 
12617c3a622dSNigel Stephens 	/* mark the TC unallocated */
12627c3a622dSNigel Stephens 	write_tc_c0_tcstatus(read_tc_c0_tcstatus() & ~TCSTATUS_A);
1263e01402b1SRalf Baechle 
1264e01402b1SRalf Baechle 	v->state = VPE_STATE_UNUSED;
1265e01402b1SRalf Baechle 
1266340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
1267e01402b1SRalf Baechle 	evpe(evpe_flags);
1268e01402b1SRalf Baechle 
1269e01402b1SRalf Baechle 	return 0;
1270e01402b1SRalf Baechle }
1271e01402b1SRalf Baechle 
1272e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_free);
1273e01402b1SRalf Baechle 
1274e01402b1SRalf Baechle void *vpe_get_shared(int index)
1275e01402b1SRalf Baechle {
1276e01402b1SRalf Baechle 	struct vpe *v;
1277e01402b1SRalf Baechle 
12782600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
1279e01402b1SRalf Baechle 		return NULL;
1280e01402b1SRalf Baechle 
1281e01402b1SRalf Baechle 	return v->shared_ptr;
1282e01402b1SRalf Baechle }
1283e01402b1SRalf Baechle 
1284e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_get_shared);
1285e01402b1SRalf Baechle 
12862600990eSRalf Baechle int vpe_getuid(int index)
12872600990eSRalf Baechle {
12882600990eSRalf Baechle 	struct vpe *v;
12892600990eSRalf Baechle 
12902600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
12912600990eSRalf Baechle 		return -1;
12922600990eSRalf Baechle 
12932600990eSRalf Baechle 	return v->uid;
12942600990eSRalf Baechle }
12952600990eSRalf Baechle 
12962600990eSRalf Baechle EXPORT_SYMBOL(vpe_getuid);
12972600990eSRalf Baechle 
12982600990eSRalf Baechle int vpe_getgid(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->gid;
13062600990eSRalf Baechle }
13072600990eSRalf Baechle 
13082600990eSRalf Baechle EXPORT_SYMBOL(vpe_getgid);
13092600990eSRalf Baechle 
13102600990eSRalf Baechle int vpe_notify(int index, struct vpe_notifications *notify)
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 	list_add(&notify->list, &v->notify);
13182600990eSRalf Baechle 	return 0;
13192600990eSRalf Baechle }
13202600990eSRalf Baechle 
13212600990eSRalf Baechle EXPORT_SYMBOL(vpe_notify);
13222600990eSRalf Baechle 
13232600990eSRalf Baechle char *vpe_getcwd(int index)
13242600990eSRalf Baechle {
13252600990eSRalf Baechle 	struct vpe *v;
13262600990eSRalf Baechle 
13272600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13282600990eSRalf Baechle 		return NULL;
13292600990eSRalf Baechle 
13302600990eSRalf Baechle 	return v->cwd;
13312600990eSRalf Baechle }
13322600990eSRalf Baechle 
13332600990eSRalf Baechle EXPORT_SYMBOL(vpe_getcwd);
13342600990eSRalf Baechle 
13352600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
13362600990eSRalf Baechle static void kspd_sp_exit( int sp_id)
13372600990eSRalf Baechle {
13382600990eSRalf Baechle 	cleanup_tc(get_tc(sp_id));
13392600990eSRalf Baechle }
13402600990eSRalf Baechle #endif
13412600990eSRalf Baechle 
1342736fad17SKay Sievers static ssize_t store_kill(struct device *dev, struct device_attribute *attr,
1343736fad17SKay Sievers 			  const char *buf, size_t len)
13440f5d0df3SRalf Baechle {
13450f5d0df3SRalf Baechle 	struct vpe *vpe = get_vpe(tclimit);
13460f5d0df3SRalf Baechle 	struct vpe_notifications *not;
13470f5d0df3SRalf Baechle 
13480f5d0df3SRalf Baechle 	list_for_each_entry(not, &vpe->notify, list) {
13490f5d0df3SRalf Baechle 		not->stop(tclimit);
13500f5d0df3SRalf Baechle 	}
13510f5d0df3SRalf Baechle 
13520f5d0df3SRalf Baechle 	release_progmem(vpe->load_addr);
13530f5d0df3SRalf Baechle 	cleanup_tc(get_tc(tclimit));
13540f5d0df3SRalf Baechle 	vpe_stop(vpe);
13550f5d0df3SRalf Baechle 	vpe_free(vpe);
13560f5d0df3SRalf Baechle 
13570f5d0df3SRalf Baechle 	return len;
13580f5d0df3SRalf Baechle }
13590f5d0df3SRalf Baechle 
1360736fad17SKay Sievers static ssize_t show_ntcs(struct device *cd, struct device_attribute *attr,
1361736fad17SKay Sievers 			 char *buf)
136241790e04SRalf Baechle {
136341790e04SRalf Baechle 	struct vpe *vpe = get_vpe(tclimit);
136441790e04SRalf Baechle 
136541790e04SRalf Baechle 	return sprintf(buf, "%d\n", vpe->ntcs);
136641790e04SRalf Baechle }
136741790e04SRalf Baechle 
1368736fad17SKay Sievers static ssize_t store_ntcs(struct device *dev, struct device_attribute *attr,
1369736fad17SKay Sievers 			  const char *buf, size_t len)
137041790e04SRalf Baechle {
137141790e04SRalf Baechle 	struct vpe *vpe = get_vpe(tclimit);
137241790e04SRalf Baechle 	unsigned long new;
137341790e04SRalf Baechle 	char *endp;
137441790e04SRalf Baechle 
137541790e04SRalf Baechle 	new = simple_strtoul(buf, &endp, 0);
137641790e04SRalf Baechle 	if (endp == buf)
137741790e04SRalf Baechle 		goto out_einval;
137841790e04SRalf Baechle 
137941790e04SRalf Baechle 	if (new == 0 || new > (hw_tcs - tclimit))
138041790e04SRalf Baechle 		goto out_einval;
138141790e04SRalf Baechle 
138241790e04SRalf Baechle 	vpe->ntcs = new;
138341790e04SRalf Baechle 
138441790e04SRalf Baechle 	return len;
138541790e04SRalf Baechle 
138641790e04SRalf Baechle out_einval:
138752a7a27cSJoe Perches 	return -EINVAL;
138841790e04SRalf Baechle }
138941790e04SRalf Baechle 
1390736fad17SKay Sievers static struct device_attribute vpe_class_attributes[] = {
13910f5d0df3SRalf Baechle 	__ATTR(kill, S_IWUSR, NULL, store_kill),
139241790e04SRalf Baechle 	__ATTR(ntcs, S_IRUGO | S_IWUSR, show_ntcs, store_ntcs),
139341790e04SRalf Baechle 	{}
139441790e04SRalf Baechle };
139541790e04SRalf Baechle 
1396736fad17SKay Sievers static void vpe_device_release(struct device *cd)
139741790e04SRalf Baechle {
139841790e04SRalf Baechle 	kfree(cd);
139941790e04SRalf Baechle }
140041790e04SRalf Baechle 
140141790e04SRalf Baechle struct class vpe_class = {
140241790e04SRalf Baechle 	.name = "vpe",
140341790e04SRalf Baechle 	.owner = THIS_MODULE,
1404736fad17SKay Sievers 	.dev_release = vpe_device_release,
1405736fad17SKay Sievers 	.dev_attrs = vpe_class_attributes,
140641790e04SRalf Baechle };
140741790e04SRalf Baechle 
1408736fad17SKay Sievers struct device vpe_device;
140927a3bbafSRalf Baechle 
1410e01402b1SRalf Baechle static int __init vpe_module_init(void)
1411e01402b1SRalf Baechle {
141207cc0c9eSRalf Baechle 	unsigned int mtflags, vpflags;
141307cc0c9eSRalf Baechle 	unsigned long flags, val;
1414e01402b1SRalf Baechle 	struct vpe *v = NULL;
1415e01402b1SRalf Baechle 	struct tc *t;
141641790e04SRalf Baechle 	int tc, err;
1417e01402b1SRalf Baechle 
1418e01402b1SRalf Baechle 	if (!cpu_has_mipsmt) {
1419e01402b1SRalf Baechle 		printk("VPE loader: not a MIPS MT capable processor\n");
1420e01402b1SRalf Baechle 		return -ENODEV;
1421e01402b1SRalf Baechle 	}
1422e01402b1SRalf Baechle 
142307cc0c9eSRalf Baechle 	if (vpelimit == 0) {
142407cc0c9eSRalf Baechle 		printk(KERN_WARNING "No VPEs reserved for AP/SP, not "
142507cc0c9eSRalf Baechle 		       "initializing VPE loader.\nPass maxvpes=<n> argument as "
142607cc0c9eSRalf Baechle 		       "kernel argument\n");
142707cc0c9eSRalf Baechle 
142807cc0c9eSRalf Baechle 		return -ENODEV;
142907cc0c9eSRalf Baechle 	}
143007cc0c9eSRalf Baechle 
143107cc0c9eSRalf Baechle 	if (tclimit == 0) {
143207cc0c9eSRalf Baechle 		printk(KERN_WARNING "No TCs reserved for AP/SP, not "
143307cc0c9eSRalf Baechle 		       "initializing VPE loader.\nPass maxtcs=<n> argument as "
143407cc0c9eSRalf Baechle 		       "kernel argument\n");
143507cc0c9eSRalf Baechle 
143607cc0c9eSRalf Baechle 		return -ENODEV;
143707cc0c9eSRalf Baechle 	}
143807cc0c9eSRalf Baechle 
1439682e852eSAlexey Dobriyan 	major = register_chrdev(0, module_name, &vpe_fops);
1440682e852eSAlexey Dobriyan 	if (major < 0) {
1441e01402b1SRalf Baechle 		printk("VPE loader: unable to register character device\n");
1442307bd284SRalf Baechle 		return major;
1443e01402b1SRalf Baechle 	}
1444e01402b1SRalf Baechle 
144541790e04SRalf Baechle 	err = class_register(&vpe_class);
144641790e04SRalf Baechle 	if (err) {
144741790e04SRalf Baechle 		printk(KERN_ERR "vpe_class registration failed\n");
144827a3bbafSRalf Baechle 		goto out_chrdev;
144927a3bbafSRalf Baechle 	}
145041790e04SRalf Baechle 
1451736fad17SKay Sievers 	device_initialize(&vpe_device);
145241790e04SRalf Baechle 	vpe_device.class	= &vpe_class,
145341790e04SRalf Baechle 	vpe_device.parent	= NULL,
14541bb5beb4SKay Sievers 	dev_set_name(&vpe_device, "vpe1");
145541790e04SRalf Baechle 	vpe_device.devt = MKDEV(major, minor);
1456736fad17SKay Sievers 	err = device_add(&vpe_device);
145741790e04SRalf Baechle 	if (err) {
145841790e04SRalf Baechle 		printk(KERN_ERR "Adding vpe_device failed\n");
145941790e04SRalf Baechle 		goto out_class;
146041790e04SRalf Baechle 	}
146127a3bbafSRalf Baechle 
146207cc0c9eSRalf Baechle 	local_irq_save(flags);
146307cc0c9eSRalf Baechle 	mtflags = dmt();
146407cc0c9eSRalf Baechle 	vpflags = dvpe();
1465e01402b1SRalf Baechle 
1466e01402b1SRalf Baechle 	/* Put MVPE's into 'configuration state' */
1467340ee4b9SRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1468e01402b1SRalf Baechle 
1469e01402b1SRalf Baechle 	/* dump_mtregs(); */
1470e01402b1SRalf Baechle 
1471e01402b1SRalf Baechle 	val = read_c0_mvpconf0();
147207cc0c9eSRalf Baechle 	hw_tcs = (val & MVPCONF0_PTC) + 1;
147307cc0c9eSRalf Baechle 	hw_vpes = ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1;
147407cc0c9eSRalf Baechle 
147507cc0c9eSRalf Baechle 	for (tc = tclimit; tc < hw_tcs; tc++) {
147607cc0c9eSRalf Baechle 		/*
147707cc0c9eSRalf Baechle 		 * Must re-enable multithreading temporarily or in case we
147807cc0c9eSRalf Baechle 		 * reschedule send IPIs or similar we might hang.
147907cc0c9eSRalf Baechle 		 */
148007cc0c9eSRalf Baechle 		clear_c0_mvpcontrol(MVPCONTROL_VPC);
148107cc0c9eSRalf Baechle 		evpe(vpflags);
148207cc0c9eSRalf Baechle 		emt(mtflags);
148307cc0c9eSRalf Baechle 		local_irq_restore(flags);
148407cc0c9eSRalf Baechle 		t = alloc_tc(tc);
148507cc0c9eSRalf Baechle 		if (!t) {
148607cc0c9eSRalf Baechle 			err = -ENOMEM;
148707cc0c9eSRalf Baechle 			goto out;
148807cc0c9eSRalf Baechle 		}
148907cc0c9eSRalf Baechle 
149007cc0c9eSRalf Baechle 		local_irq_save(flags);
149107cc0c9eSRalf Baechle 		mtflags = dmt();
149207cc0c9eSRalf Baechle 		vpflags = dvpe();
149307cc0c9eSRalf Baechle 		set_c0_mvpcontrol(MVPCONTROL_VPC);
1494e01402b1SRalf Baechle 
1495e01402b1SRalf Baechle 		/* VPE's */
149607cc0c9eSRalf Baechle 		if (tc < hw_tcs) {
149707cc0c9eSRalf Baechle 			settc(tc);
1498e01402b1SRalf Baechle 
149907cc0c9eSRalf Baechle 			if ((v = alloc_vpe(tc)) == NULL) {
1500e01402b1SRalf Baechle 				printk(KERN_WARNING "VPE: unable to allocate VPE\n");
150107cc0c9eSRalf Baechle 
150207cc0c9eSRalf Baechle 				goto out_reenable;
1503e01402b1SRalf Baechle 			}
1504e01402b1SRalf Baechle 
150541790e04SRalf Baechle 			v->ntcs = hw_tcs - tclimit;
150641790e04SRalf Baechle 
15072600990eSRalf Baechle 			/* add the tc to the list of this vpe's tc's. */
15082600990eSRalf Baechle 			list_add(&t->tc, &v->tc);
1509e01402b1SRalf Baechle 
1510e01402b1SRalf Baechle 			/* deactivate all but vpe0 */
151107cc0c9eSRalf Baechle 			if (tc >= tclimit) {
1512e01402b1SRalf Baechle 				unsigned long tmp = read_vpe_c0_vpeconf0();
1513e01402b1SRalf Baechle 
1514e01402b1SRalf Baechle 				tmp &= ~VPECONF0_VPA;
1515e01402b1SRalf Baechle 
1516e01402b1SRalf Baechle 				/* master VPE */
1517e01402b1SRalf Baechle 				tmp |= VPECONF0_MVP;
1518e01402b1SRalf Baechle 				write_vpe_c0_vpeconf0(tmp);
1519e01402b1SRalf Baechle 			}
1520e01402b1SRalf Baechle 
1521e01402b1SRalf Baechle 			/* disable multi-threading with TC's */
1522e01402b1SRalf Baechle 			write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE);
1523e01402b1SRalf Baechle 
152407cc0c9eSRalf Baechle 			if (tc >= vpelimit) {
15252600990eSRalf Baechle 				/*
15262600990eSRalf Baechle 				 * Set config to be the same as vpe0,
15272600990eSRalf Baechle 				 * particularly kseg0 coherency alg
15282600990eSRalf Baechle 				 */
1529e01402b1SRalf Baechle 				write_vpe_c0_config(read_c0_config());
1530e01402b1SRalf Baechle 			}
1531e01402b1SRalf Baechle 		}
1532e01402b1SRalf Baechle 
1533e01402b1SRalf Baechle 		/* TC's */
1534e01402b1SRalf Baechle 		t->pvpe = v;	/* set the parent vpe */
1535e01402b1SRalf Baechle 
153607cc0c9eSRalf Baechle 		if (tc >= tclimit) {
1537e01402b1SRalf Baechle 			unsigned long tmp;
1538e01402b1SRalf Baechle 
153907cc0c9eSRalf Baechle 			settc(tc);
1540e01402b1SRalf Baechle 
15412600990eSRalf Baechle 			/* Any TC that is bound to VPE0 gets left as is - in case
15422600990eSRalf Baechle 			   we are running SMTC on VPE0. A TC that is bound to any
15432600990eSRalf Baechle 			   other VPE gets bound to VPE0, ideally I'd like to make
15442600990eSRalf Baechle 			   it homeless but it doesn't appear to let me bind a TC
15452600990eSRalf Baechle 			   to a non-existent VPE. Which is perfectly reasonable.
15462600990eSRalf Baechle 
15472600990eSRalf Baechle 			   The (un)bound state is visible to an EJTAG probe so may
15482600990eSRalf Baechle 			   notify GDB...
15492600990eSRalf Baechle 			*/
15502600990eSRalf Baechle 
15512600990eSRalf Baechle 			if (((tmp = read_tc_c0_tcbind()) & TCBIND_CURVPE)) {
15522600990eSRalf Baechle 				/* tc is bound >vpe0 */
15532600990eSRalf Baechle 				write_tc_c0_tcbind(tmp & ~TCBIND_CURVPE);
15542600990eSRalf Baechle 
15552600990eSRalf Baechle 				t->pvpe = get_vpe(0);	/* set the parent vpe */
15562600990eSRalf Baechle 			}
1557e01402b1SRalf Baechle 
15587c3a622dSNigel Stephens 			/* halt the TC */
15597c3a622dSNigel Stephens 			write_tc_c0_tchalt(TCHALT_H);
15607c3a622dSNigel Stephens 			mips_ihb();
15617c3a622dSNigel Stephens 
1562e01402b1SRalf Baechle 			tmp = read_tc_c0_tcstatus();
1563e01402b1SRalf Baechle 
15642600990eSRalf Baechle 			/* mark not activated and not dynamically allocatable */
1565e01402b1SRalf Baechle 			tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1566e01402b1SRalf Baechle 			tmp |= TCSTATUS_IXMT;	/* interrupt exempt */
1567e01402b1SRalf Baechle 			write_tc_c0_tcstatus(tmp);
1568e01402b1SRalf Baechle 		}
1569e01402b1SRalf Baechle 	}
1570e01402b1SRalf Baechle 
157107cc0c9eSRalf Baechle out_reenable:
1572e01402b1SRalf Baechle 	/* release config state */
1573340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
1574e01402b1SRalf Baechle 
157507cc0c9eSRalf Baechle 	evpe(vpflags);
157607cc0c9eSRalf Baechle 	emt(mtflags);
157707cc0c9eSRalf Baechle 	local_irq_restore(flags);
157807cc0c9eSRalf Baechle 
15792600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
15802600990eSRalf Baechle 	kspd_events.kspd_sp_exit = kspd_sp_exit;
15812600990eSRalf Baechle #endif
1582e01402b1SRalf Baechle 	return 0;
158327a3bbafSRalf Baechle 
158441790e04SRalf Baechle out_class:
158541790e04SRalf Baechle 	class_unregister(&vpe_class);
158627a3bbafSRalf Baechle out_chrdev:
158727a3bbafSRalf Baechle 	unregister_chrdev(major, module_name);
158827a3bbafSRalf Baechle 
158907cc0c9eSRalf Baechle out:
159027a3bbafSRalf Baechle 	return err;
1591e01402b1SRalf Baechle }
1592e01402b1SRalf Baechle 
1593e01402b1SRalf Baechle static void __exit vpe_module_exit(void)
1594e01402b1SRalf Baechle {
1595e01402b1SRalf Baechle 	struct vpe *v, *n;
1596e01402b1SRalf Baechle 
1597e01402b1SRalf Baechle 	list_for_each_entry_safe(v, n, &vpecontrol.vpe_list, list) {
1598e01402b1SRalf Baechle 		if (v->state != VPE_STATE_UNUSED) {
1599e01402b1SRalf Baechle 			release_vpe(v);
1600e01402b1SRalf Baechle 		}
1601e01402b1SRalf Baechle 	}
1602e01402b1SRalf Baechle 
1603736fad17SKay Sievers 	device_del(&vpe_device);
1604e01402b1SRalf Baechle 	unregister_chrdev(major, module_name);
1605e01402b1SRalf Baechle }
1606e01402b1SRalf Baechle 
1607e01402b1SRalf Baechle module_init(vpe_module_init);
1608e01402b1SRalf Baechle module_exit(vpe_module_exit);
1609e01402b1SRalf Baechle MODULE_DESCRIPTION("MIPS VPE Loader");
16102600990eSRalf Baechle MODULE_AUTHOR("Elizabeth Oldham, MIPS Technologies, Inc.");
1611e01402b1SRalf Baechle MODULE_LICENSE("GPL");
1612