xref: /openbmc/linux/arch/mips/kernel/vpe.c (revision 340ee4b9)
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 /*
20e01402b1SRalf Baechle  * VPE support module
21e01402b1SRalf Baechle  *
22e01402b1SRalf Baechle  * Provides support for loading a MIPS SP program on VPE1.
23e01402b1SRalf Baechle  * The SP enviroment is rather simple, no tlb's.  It needs to be relocatable
24e01402b1SRalf Baechle  * (or partially linked). You should initialise your stack in the startup
25e01402b1SRalf Baechle  * code. This loader looks for the symbol __start and sets up
26e01402b1SRalf Baechle  * execution to resume from there. The MIPS SDE kit contains suitable examples.
27e01402b1SRalf Baechle  *
28e01402b1SRalf Baechle  * To load and run, simply cat a SP 'program file' to /dev/vpe1.
29e01402b1SRalf Baechle  * i.e cat spapp >/dev/vpe1.
30e01402b1SRalf Baechle  *
31e01402b1SRalf Baechle  * You'll need to have the following device files.
32e01402b1SRalf Baechle  * mknod /dev/vpe0 c 63 0
33e01402b1SRalf Baechle  * mknod /dev/vpe1 c 63 1
34e01402b1SRalf Baechle  */
35340ee4b9SRalf Baechle #include <linux/config.h>
36e01402b1SRalf Baechle #include <linux/kernel.h>
37e01402b1SRalf Baechle #include <linux/module.h>
38e01402b1SRalf Baechle #include <linux/fs.h>
39e01402b1SRalf Baechle #include <linux/init.h>
40e01402b1SRalf Baechle #include <asm/uaccess.h>
41e01402b1SRalf Baechle #include <linux/slab.h>
42e01402b1SRalf Baechle #include <linux/list.h>
43e01402b1SRalf Baechle #include <linux/vmalloc.h>
44e01402b1SRalf Baechle #include <linux/elf.h>
45e01402b1SRalf Baechle #include <linux/seq_file.h>
46e01402b1SRalf Baechle #include <linux/syscalls.h>
47e01402b1SRalf Baechle #include <linux/moduleloader.h>
48e01402b1SRalf Baechle #include <linux/interrupt.h>
49e01402b1SRalf Baechle #include <linux/poll.h>
50e01402b1SRalf Baechle #include <linux/bootmem.h>
51e01402b1SRalf Baechle #include <asm/mipsregs.h>
52340ee4b9SRalf Baechle #include <asm/mipsmtregs.h>
53e01402b1SRalf Baechle #include <asm/cacheflush.h>
54e01402b1SRalf Baechle #include <asm/atomic.h>
55e01402b1SRalf Baechle #include <asm/cpu.h>
56e01402b1SRalf Baechle #include <asm/processor.h>
57e01402b1SRalf Baechle #include <asm/system.h>
58e01402b1SRalf Baechle 
59e01402b1SRalf Baechle typedef void *vpe_handle;
60e01402b1SRalf Baechle 
61e01402b1SRalf Baechle // defined here because the kernel module loader doesn't have
62e01402b1SRalf Baechle // anything to do with it.
63e01402b1SRalf Baechle #define SHN_MIPS_SCOMMON 0xff03
64e01402b1SRalf Baechle 
65e01402b1SRalf Baechle #ifndef ARCH_SHF_SMALL
66e01402b1SRalf Baechle #define ARCH_SHF_SMALL 0
67e01402b1SRalf Baechle #endif
68e01402b1SRalf Baechle 
69e01402b1SRalf Baechle /* If this is set, the section belongs in the init part of the module */
70e01402b1SRalf Baechle #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
71e01402b1SRalf Baechle 
72e01402b1SRalf Baechle // temp number,
73e01402b1SRalf Baechle #define VPE_MAJOR 63
74e01402b1SRalf Baechle 
75e01402b1SRalf Baechle static char module_name[] = "vpe";
76e01402b1SRalf Baechle static int major = 0;
77e01402b1SRalf Baechle 
78e01402b1SRalf Baechle /* grab the likely amount of memory we will need. */
79e01402b1SRalf Baechle #ifdef CONFIG_MIPS_VPE_LOADER_TOM
80e01402b1SRalf Baechle #define P_SIZE (2 * 1024 * 1024)
81e01402b1SRalf Baechle #else
82e01402b1SRalf Baechle /* add an overhead to the max kmalloc size for non-striped symbols/etc */
83e01402b1SRalf Baechle #define P_SIZE (256 * 1024)
84e01402b1SRalf Baechle #endif
85e01402b1SRalf Baechle 
86e01402b1SRalf Baechle #define MAX_VPES 16
87e01402b1SRalf Baechle 
88e01402b1SRalf Baechle enum vpe_state {
89e01402b1SRalf Baechle 	VPE_STATE_UNUSED = 0,
90e01402b1SRalf Baechle 	VPE_STATE_INUSE,
91e01402b1SRalf Baechle 	VPE_STATE_RUNNING
92e01402b1SRalf Baechle };
93e01402b1SRalf Baechle 
94e01402b1SRalf Baechle enum tc_state {
95e01402b1SRalf Baechle 	TC_STATE_UNUSED = 0,
96e01402b1SRalf Baechle 	TC_STATE_INUSE,
97e01402b1SRalf Baechle 	TC_STATE_RUNNING,
98e01402b1SRalf Baechle 	TC_STATE_DYNAMIC
99e01402b1SRalf Baechle };
100e01402b1SRalf Baechle 
101e01402b1SRalf Baechle struct vpe;
102e01402b1SRalf Baechle typedef struct tc {
103e01402b1SRalf Baechle 	enum tc_state state;
104e01402b1SRalf Baechle 	int index;
105e01402b1SRalf Baechle 
106e01402b1SRalf Baechle 	/* parent VPE */
107e01402b1SRalf Baechle 	struct vpe *pvpe;
108e01402b1SRalf Baechle 
109e01402b1SRalf Baechle 	/* The list of TC's with this VPE */
110e01402b1SRalf Baechle 	struct list_head tc;
111e01402b1SRalf Baechle 
112e01402b1SRalf Baechle 	/* The global list of tc's */
113e01402b1SRalf Baechle 	struct list_head list;
114e01402b1SRalf Baechle } tc_t;
115e01402b1SRalf Baechle 
116e01402b1SRalf Baechle typedef struct vpe {
117e01402b1SRalf Baechle 	enum vpe_state state;
118e01402b1SRalf Baechle 
119e01402b1SRalf Baechle 	/* (device) minor associated with this vpe */
120e01402b1SRalf Baechle 	int minor;
121e01402b1SRalf Baechle 
122e01402b1SRalf Baechle 	/* elfloader stuff */
123e01402b1SRalf Baechle 	void *load_addr;
124e01402b1SRalf Baechle 	u32 len;
125e01402b1SRalf Baechle 	char *pbuffer;
126e01402b1SRalf Baechle 	u32 plen;
127e01402b1SRalf Baechle 
128e01402b1SRalf Baechle 	unsigned long __start;
129e01402b1SRalf Baechle 
130e01402b1SRalf Baechle 	/* tc's associated with this vpe */
131e01402b1SRalf Baechle 	struct list_head tc;
132e01402b1SRalf Baechle 
133e01402b1SRalf Baechle 	/* The list of vpe's */
134e01402b1SRalf Baechle 	struct list_head list;
135e01402b1SRalf Baechle 
136e01402b1SRalf Baechle 	/* shared symbol address */
137e01402b1SRalf Baechle 	void *shared_ptr;
138e01402b1SRalf Baechle } vpe_t;
139e01402b1SRalf Baechle 
140e01402b1SRalf Baechle struct vpecontrol_ {
141e01402b1SRalf Baechle 	/* Virtual processing elements */
142e01402b1SRalf Baechle 	struct list_head vpe_list;
143e01402b1SRalf Baechle 
144e01402b1SRalf Baechle 	/* Thread contexts */
145e01402b1SRalf Baechle 	struct list_head tc_list;
146e01402b1SRalf Baechle } vpecontrol;
147e01402b1SRalf Baechle 
148e01402b1SRalf Baechle static void release_progmem(void *ptr);
149e01402b1SRalf Baechle static void dump_vpe(vpe_t * v);
150e01402b1SRalf Baechle extern void save_gp_address(unsigned int secbase, unsigned int rel);
151e01402b1SRalf Baechle 
152e01402b1SRalf Baechle /* get the vpe associated with this minor */
153e01402b1SRalf Baechle struct vpe *get_vpe(int minor)
154e01402b1SRalf Baechle {
155e01402b1SRalf Baechle 	struct vpe *v;
156e01402b1SRalf Baechle 
157e01402b1SRalf Baechle 	list_for_each_entry(v, &vpecontrol.vpe_list, list) {
158e01402b1SRalf Baechle 		if (v->minor == minor)
159e01402b1SRalf Baechle 			return v;
160e01402b1SRalf Baechle 	}
161e01402b1SRalf Baechle 
162e01402b1SRalf Baechle 	printk(KERN_DEBUG "VPE: get_vpe minor %d not found\n", minor);
163e01402b1SRalf Baechle 	return NULL;
164e01402b1SRalf Baechle }
165e01402b1SRalf Baechle 
166e01402b1SRalf Baechle /* get the vpe associated with this minor */
167e01402b1SRalf Baechle struct tc *get_tc(int index)
168e01402b1SRalf Baechle {
169e01402b1SRalf Baechle 	struct tc *t;
170e01402b1SRalf Baechle 
171e01402b1SRalf Baechle 	list_for_each_entry(t, &vpecontrol.tc_list, list) {
172e01402b1SRalf Baechle 		if (t->index == index)
173e01402b1SRalf Baechle 			return t;
174e01402b1SRalf Baechle 	}
175e01402b1SRalf Baechle 
176e01402b1SRalf Baechle 	printk(KERN_DEBUG "VPE: get_tc index %d not found\n", index);
177e01402b1SRalf Baechle 
178e01402b1SRalf Baechle 	return NULL;
179e01402b1SRalf Baechle }
180e01402b1SRalf Baechle 
181e01402b1SRalf Baechle struct tc *get_tc_unused(void)
182e01402b1SRalf Baechle {
183e01402b1SRalf Baechle 	struct tc *t;
184e01402b1SRalf Baechle 
185e01402b1SRalf Baechle 	list_for_each_entry(t, &vpecontrol.tc_list, list) {
186e01402b1SRalf Baechle 		if (t->state == TC_STATE_UNUSED)
187e01402b1SRalf Baechle 			return t;
188e01402b1SRalf Baechle 	}
189e01402b1SRalf Baechle 
190e01402b1SRalf Baechle 	printk(KERN_DEBUG "VPE: All TC's are in use\n");
191e01402b1SRalf Baechle 
192e01402b1SRalf Baechle 	return NULL;
193e01402b1SRalf Baechle }
194e01402b1SRalf Baechle 
195e01402b1SRalf Baechle /* allocate a vpe and associate it with this minor (or index) */
196e01402b1SRalf Baechle struct vpe *alloc_vpe(int minor)
197e01402b1SRalf Baechle {
198e01402b1SRalf Baechle 	struct vpe *v;
199e01402b1SRalf Baechle 
200e01402b1SRalf Baechle 	if ((v = kmalloc(sizeof(struct vpe), GFP_KERNEL)) == NULL) {
201e01402b1SRalf Baechle 		printk(KERN_WARNING "VPE: alloc_vpe no mem\n");
202e01402b1SRalf Baechle 		return NULL;
203e01402b1SRalf Baechle 	}
204e01402b1SRalf Baechle 
205e01402b1SRalf Baechle 	memset(v, 0, sizeof(struct vpe));
206e01402b1SRalf Baechle 
207e01402b1SRalf Baechle 	INIT_LIST_HEAD(&v->tc);
208e01402b1SRalf Baechle 	list_add_tail(&v->list, &vpecontrol.vpe_list);
209e01402b1SRalf Baechle 
210e01402b1SRalf Baechle 	v->minor = minor;
211e01402b1SRalf Baechle 	return v;
212e01402b1SRalf Baechle }
213e01402b1SRalf Baechle 
214e01402b1SRalf Baechle /* allocate a tc. At startup only tc0 is running, all other can be halted. */
215e01402b1SRalf Baechle struct tc *alloc_tc(int index)
216e01402b1SRalf Baechle {
217e01402b1SRalf Baechle 	struct tc *t;
218e01402b1SRalf Baechle 
219e01402b1SRalf Baechle 	if ((t = kmalloc(sizeof(struct tc), GFP_KERNEL)) == NULL) {
220e01402b1SRalf Baechle 		printk(KERN_WARNING "VPE: alloc_tc no mem\n");
221e01402b1SRalf Baechle 		return NULL;
222e01402b1SRalf Baechle 	}
223e01402b1SRalf Baechle 
224e01402b1SRalf Baechle 	memset(t, 0, sizeof(struct tc));
225e01402b1SRalf Baechle 
226e01402b1SRalf Baechle 	INIT_LIST_HEAD(&t->tc);
227e01402b1SRalf Baechle 	list_add_tail(&t->list, &vpecontrol.tc_list);
228e01402b1SRalf Baechle 
229e01402b1SRalf Baechle 	t->index = index;
230e01402b1SRalf Baechle 
231e01402b1SRalf Baechle 	return t;
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_mvpconf0();
252e01402b1SRalf Baechle 	printk("mvpconf0 0x%lx, PVPE %ld PTC %ld M %ld\n", val,
253e01402b1SRalf Baechle 	       (val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT,
254e01402b1SRalf Baechle 	       val & MVPCONF0_PTC, (val & MVPCONF0_M) >> MVPCONF0_M_SHIFT);
255e01402b1SRalf Baechle 
256e01402b1SRalf Baechle 	val = read_c0_mvpcontrol();
257e01402b1SRalf Baechle 	printk("MVPControl 0x%lx, STLB %ld VPC %ld EVP %ld\n", val,
258e01402b1SRalf Baechle 	       (val & MVPCONTROL_STLB) >> MVPCONTROL_STLB_SHIFT,
259e01402b1SRalf Baechle 	       (val & MVPCONTROL_VPC) >> MVPCONTROL_VPC_SHIFT,
260e01402b1SRalf Baechle 	       (val & MVPCONTROL_EVP));
261e01402b1SRalf Baechle 
262e01402b1SRalf Baechle 	val = read_c0_vpeconf0();
263e01402b1SRalf Baechle 	printk("VPEConf0 0x%lx MVP %ld\n", val,
264e01402b1SRalf Baechle 	       (val & VPECONF0_MVP) >> VPECONF0_MVP_SHIFT);
265e01402b1SRalf Baechle }
266e01402b1SRalf Baechle 
267e01402b1SRalf Baechle /* Find some VPE program space  */
268e01402b1SRalf Baechle static void *alloc_progmem(u32 len)
269e01402b1SRalf Baechle {
270e01402b1SRalf Baechle #ifdef CONFIG_MIPS_VPE_LOADER_TOM
271e01402b1SRalf Baechle 	/* this means you must tell linux to use less memory than you physically have */
272e01402b1SRalf Baechle 	return (void *)((max_pfn * PAGE_SIZE) + KSEG0);
273e01402b1SRalf Baechle #else
274e01402b1SRalf Baechle 	// simple grab some mem for now
275e01402b1SRalf Baechle 	return kmalloc(len, GFP_KERNEL);
276e01402b1SRalf Baechle #endif
277e01402b1SRalf Baechle }
278e01402b1SRalf Baechle 
279e01402b1SRalf Baechle static void release_progmem(void *ptr)
280e01402b1SRalf Baechle {
281e01402b1SRalf Baechle #ifndef CONFIG_MIPS_VPE_LOADER_TOM
282e01402b1SRalf Baechle 	kfree(ptr);
283e01402b1SRalf Baechle #endif
284e01402b1SRalf Baechle }
285e01402b1SRalf Baechle 
286e01402b1SRalf Baechle /* Update size with this section: return offset. */
287e01402b1SRalf Baechle static long get_offset(unsigned long *size, Elf_Shdr * sechdr)
288e01402b1SRalf Baechle {
289e01402b1SRalf Baechle 	long ret;
290e01402b1SRalf Baechle 
291e01402b1SRalf Baechle 	ret = ALIGN(*size, sechdr->sh_addralign ? : 1);
292e01402b1SRalf Baechle 	*size = ret + sechdr->sh_size;
293e01402b1SRalf Baechle 	return ret;
294e01402b1SRalf Baechle }
295e01402b1SRalf Baechle 
296e01402b1SRalf Baechle /* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld
297e01402b1SRalf Baechle    might -- code, read-only data, read-write data, small data.  Tally
298e01402b1SRalf Baechle    sizes, and place the offsets into sh_entsize fields: high bit means it
299e01402b1SRalf Baechle    belongs in init. */
300e01402b1SRalf Baechle static void layout_sections(struct module *mod, const Elf_Ehdr * hdr,
301e01402b1SRalf Baechle 			    Elf_Shdr * sechdrs, const char *secstrings)
302e01402b1SRalf Baechle {
303e01402b1SRalf Baechle 	static unsigned long const masks[][2] = {
304e01402b1SRalf Baechle 		/* NOTE: all executable code must be the first section
305e01402b1SRalf Baechle 		 * in this array; otherwise modify the text_size
306e01402b1SRalf Baechle 		 * finder in the two loops below */
307e01402b1SRalf Baechle 		{SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL},
308e01402b1SRalf Baechle 		{SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL},
309e01402b1SRalf Baechle 		{SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL},
310e01402b1SRalf Baechle 		{ARCH_SHF_SMALL | SHF_ALLOC, 0}
311e01402b1SRalf Baechle 	};
312e01402b1SRalf Baechle 	unsigned int m, i;
313e01402b1SRalf Baechle 
314e01402b1SRalf Baechle 	for (i = 0; i < hdr->e_shnum; i++)
315e01402b1SRalf Baechle 		sechdrs[i].sh_entsize = ~0UL;
316e01402b1SRalf Baechle 
317e01402b1SRalf Baechle 	for (m = 0; m < ARRAY_SIZE(masks); ++m) {
318e01402b1SRalf Baechle 		for (i = 0; i < hdr->e_shnum; ++i) {
319e01402b1SRalf Baechle 			Elf_Shdr *s = &sechdrs[i];
320e01402b1SRalf Baechle 
321e01402b1SRalf Baechle 			//  || strncmp(secstrings + s->sh_name, ".init", 5) == 0)
322e01402b1SRalf Baechle 			if ((s->sh_flags & masks[m][0]) != masks[m][0]
323e01402b1SRalf Baechle 			    || (s->sh_flags & masks[m][1])
324e01402b1SRalf Baechle 			    || s->sh_entsize != ~0UL)
325e01402b1SRalf Baechle 				continue;
326e01402b1SRalf Baechle 			s->sh_entsize = get_offset(&mod->core_size, s);
327e01402b1SRalf Baechle 		}
328e01402b1SRalf Baechle 
329e01402b1SRalf Baechle 		if (m == 0)
330e01402b1SRalf Baechle 			mod->core_text_size = mod->core_size;
331e01402b1SRalf Baechle 
332e01402b1SRalf Baechle 	}
333e01402b1SRalf Baechle }
334e01402b1SRalf Baechle 
335e01402b1SRalf Baechle 
336e01402b1SRalf Baechle /* from module-elf32.c, but subverted a little */
337e01402b1SRalf Baechle 
338e01402b1SRalf Baechle struct mips_hi16 {
339e01402b1SRalf Baechle 	struct mips_hi16 *next;
340e01402b1SRalf Baechle 	Elf32_Addr *addr;
341e01402b1SRalf Baechle 	Elf32_Addr value;
342e01402b1SRalf Baechle };
343e01402b1SRalf Baechle 
344e01402b1SRalf Baechle static struct mips_hi16 *mips_hi16_list;
345e01402b1SRalf Baechle static unsigned int gp_offs, gp_addr;
346e01402b1SRalf Baechle 
347e01402b1SRalf Baechle static int apply_r_mips_none(struct module *me, uint32_t *location,
348e01402b1SRalf Baechle 			     Elf32_Addr v)
349e01402b1SRalf Baechle {
350e01402b1SRalf Baechle 	return 0;
351e01402b1SRalf Baechle }
352e01402b1SRalf Baechle 
353e01402b1SRalf Baechle static int apply_r_mips_gprel16(struct module *me, uint32_t *location,
354e01402b1SRalf Baechle 				Elf32_Addr v)
355e01402b1SRalf Baechle {
356e01402b1SRalf Baechle 	int rel;
357e01402b1SRalf Baechle 
358e01402b1SRalf Baechle 	if( !(*location & 0xffff) ) {
359e01402b1SRalf Baechle 		rel = (int)v - gp_addr;
360e01402b1SRalf Baechle 	}
361e01402b1SRalf Baechle 	else {
362e01402b1SRalf Baechle 		/* .sbss + gp(relative) + offset */
363e01402b1SRalf Baechle 		/* kludge! */
364e01402b1SRalf Baechle 		rel =  (int)(short)((int)v + gp_offs +
365e01402b1SRalf Baechle 				    (int)(short)(*location & 0xffff) - gp_addr);
366e01402b1SRalf Baechle 	}
367e01402b1SRalf Baechle 
368e01402b1SRalf Baechle 	if( (rel > 32768) || (rel < -32768) ) {
369e01402b1SRalf Baechle 		printk(KERN_ERR
370e01402b1SRalf Baechle 		       "apply_r_mips_gprel16: relative address out of range 0x%x %d\n",
371e01402b1SRalf Baechle 		       rel, rel);
372e01402b1SRalf Baechle 		return -ENOEXEC;
373e01402b1SRalf Baechle 	}
374e01402b1SRalf Baechle 
375e01402b1SRalf Baechle 	*location = (*location & 0xffff0000) | (rel & 0xffff);
376e01402b1SRalf Baechle 
377e01402b1SRalf Baechle 	return 0;
378e01402b1SRalf Baechle }
379e01402b1SRalf Baechle 
380e01402b1SRalf Baechle static int apply_r_mips_pc16(struct module *me, uint32_t *location,
381e01402b1SRalf Baechle 			     Elf32_Addr v)
382e01402b1SRalf Baechle {
383e01402b1SRalf Baechle 	int rel;
384e01402b1SRalf Baechle 	rel = (((unsigned int)v - (unsigned int)location));
385e01402b1SRalf Baechle 	rel >>= 2;		// because the offset is in _instructions_ not bytes.
386e01402b1SRalf Baechle 	rel -= 1;		// and one instruction less due to the branch delay slot.
387e01402b1SRalf Baechle 
388e01402b1SRalf Baechle 	if( (rel > 32768) || (rel < -32768) ) {
389e01402b1SRalf Baechle 		printk(KERN_ERR
390e01402b1SRalf Baechle 		       "apply_r_mips_pc16: relative address out of range 0x%x\n", rel);
391e01402b1SRalf Baechle 		return -ENOEXEC;
392e01402b1SRalf Baechle 	}
393e01402b1SRalf Baechle 
394e01402b1SRalf Baechle 	*location = (*location & 0xffff0000) | (rel & 0xffff);
395e01402b1SRalf Baechle 
396e01402b1SRalf Baechle 	return 0;
397e01402b1SRalf Baechle }
398e01402b1SRalf Baechle 
399e01402b1SRalf Baechle static int apply_r_mips_32(struct module *me, uint32_t *location,
400e01402b1SRalf Baechle 			   Elf32_Addr v)
401e01402b1SRalf Baechle {
402e01402b1SRalf Baechle 	*location += v;
403e01402b1SRalf Baechle 
404e01402b1SRalf Baechle 	return 0;
405e01402b1SRalf Baechle }
406e01402b1SRalf Baechle 
407e01402b1SRalf Baechle static int apply_r_mips_26(struct module *me, uint32_t *location,
408e01402b1SRalf Baechle 			   Elf32_Addr v)
409e01402b1SRalf Baechle {
410e01402b1SRalf Baechle 	if (v % 4) {
411e01402b1SRalf Baechle 		printk(KERN_ERR "module %s: dangerous relocation mod4\n", me->name);
412e01402b1SRalf Baechle 		return -ENOEXEC;
413e01402b1SRalf Baechle 	}
414e01402b1SRalf Baechle 
415e01402b1SRalf Baechle /* Not desperately convinced this is a good check of an overflow condition
416e01402b1SRalf Baechle    anyway. But it gets in the way of handling undefined weak symbols which
417e01402b1SRalf Baechle    we want to set to zero.
418e01402b1SRalf Baechle    if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) {
419e01402b1SRalf Baechle    printk(KERN_ERR
420e01402b1SRalf Baechle    "module %s: relocation overflow\n",
421e01402b1SRalf Baechle    me->name);
422e01402b1SRalf Baechle    return -ENOEXEC;
423e01402b1SRalf Baechle    }
424e01402b1SRalf Baechle */
425e01402b1SRalf Baechle 
426e01402b1SRalf Baechle 	*location = (*location & ~0x03ffffff) |
427e01402b1SRalf Baechle 		((*location + (v >> 2)) & 0x03ffffff);
428e01402b1SRalf Baechle 	return 0;
429e01402b1SRalf Baechle }
430e01402b1SRalf Baechle 
431e01402b1SRalf Baechle static int apply_r_mips_hi16(struct module *me, uint32_t *location,
432e01402b1SRalf Baechle 			     Elf32_Addr v)
433e01402b1SRalf Baechle {
434e01402b1SRalf Baechle 	struct mips_hi16 *n;
435e01402b1SRalf Baechle 
436e01402b1SRalf Baechle 	/*
437e01402b1SRalf Baechle 	 * We cannot relocate this one now because we don't know the value of
438e01402b1SRalf Baechle 	 * the carry we need to add.  Save the information, and let LO16 do the
439e01402b1SRalf Baechle 	 * actual relocation.
440e01402b1SRalf Baechle 	 */
441e01402b1SRalf Baechle 	n = kmalloc(sizeof *n, GFP_KERNEL);
442e01402b1SRalf Baechle 	if (!n)
443e01402b1SRalf Baechle 		return -ENOMEM;
444e01402b1SRalf Baechle 
445e01402b1SRalf Baechle 	n->addr = location;
446e01402b1SRalf Baechle 	n->value = v;
447e01402b1SRalf Baechle 	n->next = mips_hi16_list;
448e01402b1SRalf Baechle 	mips_hi16_list = n;
449e01402b1SRalf Baechle 
450e01402b1SRalf Baechle 	return 0;
451e01402b1SRalf Baechle }
452e01402b1SRalf Baechle 
453e01402b1SRalf Baechle static int apply_r_mips_lo16(struct module *me, uint32_t *location,
454e01402b1SRalf Baechle 			     Elf32_Addr v)
455e01402b1SRalf Baechle {
456e01402b1SRalf Baechle 	unsigned long insnlo = *location;
457e01402b1SRalf Baechle 	Elf32_Addr val, vallo;
458e01402b1SRalf Baechle 
459e01402b1SRalf Baechle 	/* Sign extend the addend we extract from the lo insn.  */
460e01402b1SRalf Baechle 	vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;
461e01402b1SRalf Baechle 
462e01402b1SRalf Baechle 	if (mips_hi16_list != NULL) {
463e01402b1SRalf Baechle 		struct mips_hi16 *l;
464e01402b1SRalf Baechle 
465e01402b1SRalf Baechle 		l = mips_hi16_list;
466e01402b1SRalf Baechle 		while (l != NULL) {
467e01402b1SRalf Baechle 			struct mips_hi16 *next;
468e01402b1SRalf Baechle 			unsigned long insn;
469e01402b1SRalf Baechle 
470e01402b1SRalf Baechle 			/*
471e01402b1SRalf Baechle 			 * The value for the HI16 had best be the same.
472e01402b1SRalf Baechle 			 */
473e01402b1SRalf Baechle 			if (v != l->value) {
474e01402b1SRalf Baechle 				printk("%d != %d\n", v, l->value);
475e01402b1SRalf Baechle 				goto out_danger;
476e01402b1SRalf Baechle 			}
477e01402b1SRalf Baechle 
478e01402b1SRalf Baechle 
479e01402b1SRalf Baechle 			/*
480e01402b1SRalf Baechle 			 * Do the HI16 relocation.  Note that we actually don't
481e01402b1SRalf Baechle 			 * need to know anything about the LO16 itself, except
482e01402b1SRalf Baechle 			 * where to find the low 16 bits of the addend needed
483e01402b1SRalf Baechle 			 * by the LO16.
484e01402b1SRalf Baechle 			 */
485e01402b1SRalf Baechle 			insn = *l->addr;
486e01402b1SRalf Baechle 			val = ((insn & 0xffff) << 16) + vallo;
487e01402b1SRalf Baechle 			val += v;
488e01402b1SRalf Baechle 
489e01402b1SRalf Baechle 			/*
490e01402b1SRalf Baechle 			 * Account for the sign extension that will happen in
491e01402b1SRalf Baechle 			 * the low bits.
492e01402b1SRalf Baechle 			 */
493e01402b1SRalf Baechle 			val = ((val >> 16) + ((val & 0x8000) != 0)) & 0xffff;
494e01402b1SRalf Baechle 
495e01402b1SRalf Baechle 			insn = (insn & ~0xffff) | val;
496e01402b1SRalf Baechle 			*l->addr = insn;
497e01402b1SRalf Baechle 
498e01402b1SRalf Baechle 			next = l->next;
499e01402b1SRalf Baechle 			kfree(l);
500e01402b1SRalf Baechle 			l = next;
501e01402b1SRalf Baechle 		}
502e01402b1SRalf Baechle 
503e01402b1SRalf Baechle 		mips_hi16_list = NULL;
504e01402b1SRalf Baechle 	}
505e01402b1SRalf Baechle 
506e01402b1SRalf Baechle 	/*
507e01402b1SRalf Baechle 	 * Ok, we're done with the HI16 relocs.  Now deal with the LO16.
508e01402b1SRalf Baechle 	 */
509e01402b1SRalf Baechle 	val = v + vallo;
510e01402b1SRalf Baechle 	insnlo = (insnlo & ~0xffff) | (val & 0xffff);
511e01402b1SRalf Baechle 	*location = insnlo;
512e01402b1SRalf Baechle 
513e01402b1SRalf Baechle 	return 0;
514e01402b1SRalf Baechle 
515e01402b1SRalf Baechle out_danger:
516e01402b1SRalf Baechle 	printk(KERN_ERR "module %s: dangerous " "relocation\n", me->name);
517e01402b1SRalf Baechle 
518e01402b1SRalf Baechle 	return -ENOEXEC;
519e01402b1SRalf Baechle }
520e01402b1SRalf Baechle 
521e01402b1SRalf Baechle static int (*reloc_handlers[]) (struct module *me, uint32_t *location,
522e01402b1SRalf Baechle 				Elf32_Addr v) = {
523e01402b1SRalf Baechle 	[R_MIPS_NONE]	= apply_r_mips_none,
524e01402b1SRalf Baechle 	[R_MIPS_32]	= apply_r_mips_32,
525e01402b1SRalf Baechle 	[R_MIPS_26]	= apply_r_mips_26,
526e01402b1SRalf Baechle 	[R_MIPS_HI16]	= apply_r_mips_hi16,
527e01402b1SRalf Baechle 	[R_MIPS_LO16]	= apply_r_mips_lo16,
528e01402b1SRalf Baechle 	[R_MIPS_GPREL16] = apply_r_mips_gprel16,
529e01402b1SRalf Baechle 	[R_MIPS_PC16] = apply_r_mips_pc16
530e01402b1SRalf Baechle };
531e01402b1SRalf Baechle 
532e01402b1SRalf Baechle 
533e01402b1SRalf Baechle int apply_relocations(Elf32_Shdr *sechdrs,
534e01402b1SRalf Baechle 		      const char *strtab,
535e01402b1SRalf Baechle 		      unsigned int symindex,
536e01402b1SRalf Baechle 		      unsigned int relsec,
537e01402b1SRalf Baechle 		      struct module *me)
538e01402b1SRalf Baechle {
539e01402b1SRalf Baechle 	Elf32_Rel *rel = (void *) sechdrs[relsec].sh_addr;
540e01402b1SRalf Baechle 	Elf32_Sym *sym;
541e01402b1SRalf Baechle 	uint32_t *location;
542e01402b1SRalf Baechle 	unsigned int i;
543e01402b1SRalf Baechle 	Elf32_Addr v;
544e01402b1SRalf Baechle 	int res;
545e01402b1SRalf Baechle 
546e01402b1SRalf Baechle 	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
547e01402b1SRalf Baechle 		Elf32_Word r_info = rel[i].r_info;
548e01402b1SRalf Baechle 
549e01402b1SRalf Baechle 		/* This is where to make the change */
550e01402b1SRalf Baechle 		location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
551e01402b1SRalf Baechle 			+ rel[i].r_offset;
552e01402b1SRalf Baechle 		/* This is the symbol it is referring to */
553e01402b1SRalf Baechle 		sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
554e01402b1SRalf Baechle 			+ ELF32_R_SYM(r_info);
555e01402b1SRalf Baechle 
556e01402b1SRalf Baechle 		if (!sym->st_value) {
557e01402b1SRalf Baechle 			printk(KERN_DEBUG "%s: undefined weak symbol %s\n",
558e01402b1SRalf Baechle 			       me->name, strtab + sym->st_name);
559e01402b1SRalf Baechle 			/* just print the warning, dont barf */
560e01402b1SRalf Baechle 		}
561e01402b1SRalf Baechle 
562e01402b1SRalf Baechle 		v = sym->st_value;
563e01402b1SRalf Baechle 
564e01402b1SRalf Baechle 		res = reloc_handlers[ELF32_R_TYPE(r_info)](me, location, v);
565e01402b1SRalf Baechle 		if( res ) {
566e01402b1SRalf Baechle 			printk(KERN_DEBUG
567e01402b1SRalf Baechle 			       "relocation error 0x%x sym refer <%s> value 0x%x "
568e01402b1SRalf Baechle 			       "type 0x%x r_info 0x%x\n",
569e01402b1SRalf Baechle 			       (unsigned int)location, strtab + sym->st_name, v,
570e01402b1SRalf Baechle 			       r_info, ELF32_R_TYPE(r_info));
571e01402b1SRalf Baechle 		}
572e01402b1SRalf Baechle 
573e01402b1SRalf Baechle 		if (res)
574e01402b1SRalf Baechle 			return res;
575e01402b1SRalf Baechle 	}
576e01402b1SRalf Baechle 
577e01402b1SRalf Baechle 	return 0;
578e01402b1SRalf Baechle }
579e01402b1SRalf Baechle 
580e01402b1SRalf Baechle void save_gp_address(unsigned int secbase, unsigned int rel)
581e01402b1SRalf Baechle {
582e01402b1SRalf Baechle 	gp_addr = secbase + rel;
583e01402b1SRalf Baechle 	gp_offs = gp_addr - (secbase & 0xffff0000);
584e01402b1SRalf Baechle }
585e01402b1SRalf Baechle /* end module-elf32.c */
586e01402b1SRalf Baechle 
587e01402b1SRalf Baechle 
588e01402b1SRalf Baechle 
589e01402b1SRalf Baechle /* Change all symbols so that sh_value encodes the pointer directly. */
590e01402b1SRalf Baechle static int simplify_symbols(Elf_Shdr * sechdrs,
591e01402b1SRalf Baechle 			    unsigned int symindex,
592e01402b1SRalf Baechle 			    const char *strtab,
593e01402b1SRalf Baechle 			    const char *secstrings,
594e01402b1SRalf Baechle 			    unsigned int nsecs, struct module *mod)
595e01402b1SRalf Baechle {
596e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
597e01402b1SRalf Baechle 	unsigned long secbase, bssbase = 0;
598e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
599e01402b1SRalf Baechle 	int ret = 0, size;
600e01402b1SRalf Baechle 
601e01402b1SRalf Baechle 	/* find the .bss section for COMMON symbols */
602e01402b1SRalf Baechle 	for (i = 0; i < nsecs; i++) {
603e01402b1SRalf Baechle 		if (strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) == 0)
604e01402b1SRalf Baechle 			bssbase = sechdrs[i].sh_addr;
605e01402b1SRalf Baechle 	}
606e01402b1SRalf Baechle 
607e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
608e01402b1SRalf Baechle 		switch (sym[i].st_shndx) {
609e01402b1SRalf Baechle 		case SHN_COMMON:
610e01402b1SRalf Baechle 			/* Allocate space for the symbol in the .bss section. st_value is currently size.
611e01402b1SRalf Baechle 			   We want it to have the address of the symbol. */
612e01402b1SRalf Baechle 
613e01402b1SRalf Baechle 			size = sym[i].st_value;
614e01402b1SRalf Baechle 			sym[i].st_value = bssbase;
615e01402b1SRalf Baechle 
616e01402b1SRalf Baechle 			bssbase += size;
617e01402b1SRalf Baechle 			break;
618e01402b1SRalf Baechle 
619e01402b1SRalf Baechle 		case SHN_ABS:
620e01402b1SRalf Baechle 			/* Don't need to do anything */
621e01402b1SRalf Baechle 			break;
622e01402b1SRalf Baechle 
623e01402b1SRalf Baechle 		case SHN_UNDEF:
624e01402b1SRalf Baechle 			/* ret = -ENOENT; */
625e01402b1SRalf Baechle 			break;
626e01402b1SRalf Baechle 
627e01402b1SRalf Baechle 		case SHN_MIPS_SCOMMON:
628e01402b1SRalf Baechle 
629e01402b1SRalf Baechle 			printk(KERN_DEBUG
630e01402b1SRalf Baechle 			       "simplify_symbols: ignoring SHN_MIPS_SCOMMON symbol <%s> st_shndx %d\n",
631e01402b1SRalf Baechle 			       strtab + sym[i].st_name, sym[i].st_shndx);
632e01402b1SRalf Baechle 
633e01402b1SRalf Baechle 			// .sbss section
634e01402b1SRalf Baechle 			break;
635e01402b1SRalf Baechle 
636e01402b1SRalf Baechle 		default:
637e01402b1SRalf Baechle 			secbase = sechdrs[sym[i].st_shndx].sh_addr;
638e01402b1SRalf Baechle 
639e01402b1SRalf Baechle 			if (strncmp(strtab + sym[i].st_name, "_gp", 3) == 0) {
640e01402b1SRalf Baechle 				save_gp_address(secbase, sym[i].st_value);
641e01402b1SRalf Baechle 			}
642e01402b1SRalf Baechle 
643e01402b1SRalf Baechle 			sym[i].st_value += secbase;
644e01402b1SRalf Baechle 			break;
645e01402b1SRalf Baechle 		}
646e01402b1SRalf Baechle 
647e01402b1SRalf Baechle 	}
648e01402b1SRalf Baechle 
649e01402b1SRalf Baechle 	return ret;
650e01402b1SRalf Baechle }
651e01402b1SRalf Baechle 
652e01402b1SRalf Baechle #ifdef DEBUG_ELFLOADER
653e01402b1SRalf Baechle static void dump_elfsymbols(Elf_Shdr * sechdrs, unsigned int symindex,
654e01402b1SRalf Baechle 			    const char *strtab, struct module *mod)
655e01402b1SRalf Baechle {
656e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
657e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
658e01402b1SRalf Baechle 
659e01402b1SRalf Baechle 	printk(KERN_DEBUG "dump_elfsymbols: n %d\n", n);
660e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
661e01402b1SRalf Baechle 		printk(KERN_DEBUG " i %d name <%s> 0x%x\n", i,
662e01402b1SRalf Baechle 		       strtab + sym[i].st_name, sym[i].st_value);
663e01402b1SRalf Baechle 	}
664e01402b1SRalf Baechle }
665e01402b1SRalf Baechle #endif
666e01402b1SRalf Baechle 
667e01402b1SRalf Baechle static void dump_tc(struct tc *t)
668e01402b1SRalf Baechle {
669e01402b1SRalf Baechle 	printk(KERN_WARNING "VPE: TC index %d TCStatus 0x%lx halt 0x%lx\n",
670e01402b1SRalf Baechle 	       t->index, read_tc_c0_tcstatus(), read_tc_c0_tchalt());
671e01402b1SRalf Baechle 	printk(KERN_WARNING "VPE: tcrestart 0x%lx\n", read_tc_c0_tcrestart());
672e01402b1SRalf Baechle }
673e01402b1SRalf Baechle 
674e01402b1SRalf Baechle static void dump_tclist(void)
675e01402b1SRalf Baechle {
676e01402b1SRalf Baechle 	struct tc *t;
677e01402b1SRalf Baechle 
678e01402b1SRalf Baechle 	list_for_each_entry(t, &vpecontrol.tc_list, list) {
679e01402b1SRalf Baechle 		dump_tc(t);
680e01402b1SRalf Baechle 	}
681e01402b1SRalf Baechle }
682e01402b1SRalf Baechle 
683e01402b1SRalf Baechle /* We are prepared so configure and start the VPE... */
684e01402b1SRalf Baechle int vpe_run(vpe_t * v)
685e01402b1SRalf Baechle {
686e01402b1SRalf Baechle 	unsigned long val;
687e01402b1SRalf Baechle 	struct tc *t;
688e01402b1SRalf Baechle 
689e01402b1SRalf Baechle 	/* check we are the Master VPE */
690e01402b1SRalf Baechle 	val = read_c0_vpeconf0();
691e01402b1SRalf Baechle 	if (!(val & VPECONF0_MVP)) {
692e01402b1SRalf Baechle 		printk(KERN_WARNING
693e01402b1SRalf Baechle 		       "VPE: only Master VPE's are allowed to configure MT\n");
694e01402b1SRalf Baechle 		return -1;
695e01402b1SRalf Baechle 	}
696e01402b1SRalf Baechle 
697e01402b1SRalf Baechle 	/* disable MT (using dvpe) */
698e01402b1SRalf Baechle 	dvpe();
699e01402b1SRalf Baechle 
700e01402b1SRalf Baechle 	/* Put MVPE's into 'configuration state' */
701340ee4b9SRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
702e01402b1SRalf Baechle 
703e01402b1SRalf Baechle 	if (!list_empty(&v->tc)) {
704e01402b1SRalf Baechle 		if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
705e01402b1SRalf Baechle 			printk(KERN_WARNING "VPE: TC %d is already in use.\n",
706e01402b1SRalf Baechle 			       t->index);
707e01402b1SRalf Baechle 			return -ENOEXEC;
708e01402b1SRalf Baechle 		}
709e01402b1SRalf Baechle 	} else {
710e01402b1SRalf Baechle 		printk(KERN_WARNING "VPE: No TC's associated with VPE %d\n",
711e01402b1SRalf Baechle 		       v->minor);
712e01402b1SRalf Baechle 		return -ENOEXEC;
713e01402b1SRalf Baechle 	}
714e01402b1SRalf Baechle 
715e01402b1SRalf Baechle 	settc(t->index);
716e01402b1SRalf Baechle 
717e01402b1SRalf Baechle 	val = read_vpe_c0_vpeconf0();
718e01402b1SRalf Baechle 
719e01402b1SRalf Baechle 	/* should check it is halted, and not activated */
720e01402b1SRalf Baechle 	if ((read_tc_c0_tcstatus() & TCSTATUS_A) || !(read_tc_c0_tchalt() & TCHALT_H)) {
721e01402b1SRalf Baechle 		printk(KERN_WARNING "VPE: TC %d is already doing something!\n",
722e01402b1SRalf Baechle 		       t->index);
723e01402b1SRalf Baechle 
724e01402b1SRalf Baechle 		dump_tclist();
725e01402b1SRalf Baechle 		return -ENOEXEC;
726e01402b1SRalf Baechle 	}
727e01402b1SRalf Baechle 
728e01402b1SRalf Baechle 	/* Write the address we want it to start running from in the TCPC register. */
729e01402b1SRalf Baechle 	write_tc_c0_tcrestart((unsigned long)v->__start);
730e01402b1SRalf Baechle 
731e01402b1SRalf Baechle 	/* write the sivc_info address to tccontext */
732e01402b1SRalf Baechle 	write_tc_c0_tccontext((unsigned long)0);
733e01402b1SRalf Baechle 
734e01402b1SRalf Baechle 	/* Set up the XTC bit in vpeconf0 to point at our tc */
735e01402b1SRalf Baechle 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | (t->index << VPECONF0_XTC_SHIFT));
736e01402b1SRalf Baechle 
737e01402b1SRalf Baechle 	/* mark the TC as activated, not interrupt exempt and not dynamically allocatable */
738e01402b1SRalf Baechle 	val = read_tc_c0_tcstatus();
739e01402b1SRalf Baechle 	val = (val & ~(TCSTATUS_DA | TCSTATUS_IXMT)) | TCSTATUS_A;
740e01402b1SRalf Baechle 	write_tc_c0_tcstatus(val);
741e01402b1SRalf Baechle 
742e01402b1SRalf Baechle 	write_tc_c0_tchalt(read_tc_c0_tchalt() & ~TCHALT_H);
743e01402b1SRalf Baechle 
744e01402b1SRalf Baechle 	/* set up VPE1 */
745e01402b1SRalf Baechle 	write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE);	// no multiple TC's
746e01402b1SRalf Baechle 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA);	// enable this VPE
747e01402b1SRalf Baechle 
748e01402b1SRalf Baechle 	/*
749e01402b1SRalf Baechle 	 * The sde-kit passes 'memsize' to __start in $a3, so set something
750e01402b1SRalf Baechle 	 * here...
751e01402b1SRalf Baechle 	 * Or set $a3 (register 7) to zero and define DFLT_STACK_SIZE and
752e01402b1SRalf Baechle 	 * DFLT_HEAP_SIZE when you compile your program
753e01402b1SRalf Baechle 	 */
754e01402b1SRalf Baechle 
755e01402b1SRalf Baechle 	mttgpr(7, 0);
756e01402b1SRalf Baechle 
757e01402b1SRalf Baechle 	/* set config to be the same as vpe0, particularly kseg0 coherency alg */
758e01402b1SRalf Baechle 	write_vpe_c0_config(read_c0_config());
759e01402b1SRalf Baechle 
760e01402b1SRalf Baechle 	/* clear out any left overs from a previous program */
761e01402b1SRalf Baechle 	write_vpe_c0_cause(0);
762e01402b1SRalf Baechle 
763e01402b1SRalf Baechle 	/* take system out of configuration state */
764340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
765e01402b1SRalf Baechle 
766e01402b1SRalf Baechle 	/* clear interrupts enabled IE, ERL, EXL, and KSU from c0 status */
767e01402b1SRalf Baechle 	write_vpe_c0_status(read_vpe_c0_status() & ~(ST0_ERL | ST0_KSU | ST0_IE | ST0_EXL));
768e01402b1SRalf Baechle 
769e01402b1SRalf Baechle 	/* set it running */
770e01402b1SRalf Baechle 	evpe(EVPE_ENABLE);
771e01402b1SRalf Baechle 
772e01402b1SRalf Baechle 	return 0;
773e01402b1SRalf Baechle }
774e01402b1SRalf Baechle 
775e01402b1SRalf Baechle static unsigned long find_vpe_symbols(vpe_t * v, Elf_Shdr * sechdrs,
776e01402b1SRalf Baechle 				      unsigned int symindex, const char *strtab,
777e01402b1SRalf Baechle 				      struct module *mod)
778e01402b1SRalf Baechle {
779e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
780e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
781e01402b1SRalf Baechle 
782e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
783e01402b1SRalf Baechle 		if (strcmp(strtab + sym[i].st_name, "__start") == 0) {
784e01402b1SRalf Baechle 			v->__start = sym[i].st_value;
785e01402b1SRalf Baechle 		}
786e01402b1SRalf Baechle 
787e01402b1SRalf Baechle 		if (strcmp(strtab + sym[i].st_name, "vpe_shared") == 0) {
788e01402b1SRalf Baechle 			v->shared_ptr = (void *)sym[i].st_value;
789e01402b1SRalf Baechle 		}
790e01402b1SRalf Baechle 	}
791e01402b1SRalf Baechle 
792e01402b1SRalf Baechle 	return 0;
793e01402b1SRalf Baechle }
794e01402b1SRalf Baechle 
795e01402b1SRalf Baechle /* Allocates a VPE with some program code space(the load address), copies the contents
796e01402b1SRalf Baechle    of the program (p)buffer performing relocatations/etc, free's it when finished.
797e01402b1SRalf Baechle */
798e01402b1SRalf Baechle int vpe_elfload(vpe_t * v)
799e01402b1SRalf Baechle {
800e01402b1SRalf Baechle 	Elf_Ehdr *hdr;
801e01402b1SRalf Baechle 	Elf_Shdr *sechdrs;
802e01402b1SRalf Baechle 	long err = 0;
803e01402b1SRalf Baechle 	char *secstrings, *strtab = NULL;
804e01402b1SRalf Baechle 	unsigned int len, i, symindex = 0, strindex = 0;
805e01402b1SRalf Baechle 
806e01402b1SRalf Baechle 	struct module mod;	// so we can re-use the relocations code
807e01402b1SRalf Baechle 
808e01402b1SRalf Baechle 	memset(&mod, 0, sizeof(struct module));
809e01402b1SRalf Baechle 	strcpy(mod.name, "VPE dummy prog module");
810e01402b1SRalf Baechle 
811e01402b1SRalf Baechle 	hdr = (Elf_Ehdr *) v->pbuffer;
812e01402b1SRalf Baechle 	len = v->plen;
813e01402b1SRalf Baechle 
814e01402b1SRalf Baechle 	/* Sanity checks against insmoding binaries or wrong arch,
815e01402b1SRalf Baechle 	   weird elf version */
816e01402b1SRalf Baechle 	if (memcmp(hdr->e_ident, ELFMAG, 4) != 0
817e01402b1SRalf Baechle 	    || hdr->e_type != ET_REL || !elf_check_arch(hdr)
818e01402b1SRalf Baechle 	    || hdr->e_shentsize != sizeof(*sechdrs)) {
819e01402b1SRalf Baechle 		printk(KERN_WARNING
820e01402b1SRalf Baechle 		       "VPE program, wrong arch or weird elf version\n");
821e01402b1SRalf Baechle 
822e01402b1SRalf Baechle 		return -ENOEXEC;
823e01402b1SRalf Baechle 	}
824e01402b1SRalf Baechle 
825e01402b1SRalf Baechle 	if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) {
826e01402b1SRalf Baechle 		printk(KERN_ERR "VPE program length %u truncated\n", len);
827e01402b1SRalf Baechle 		return -ENOEXEC;
828e01402b1SRalf Baechle 	}
829e01402b1SRalf Baechle 
830e01402b1SRalf Baechle 	/* Convenience variables */
831e01402b1SRalf Baechle 	sechdrs = (void *)hdr + hdr->e_shoff;
832e01402b1SRalf Baechle 	secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
833e01402b1SRalf Baechle 	sechdrs[0].sh_addr = 0;
834e01402b1SRalf Baechle 
835e01402b1SRalf Baechle 	/* And these should exist, but gcc whinges if we don't init them */
836e01402b1SRalf Baechle 	symindex = strindex = 0;
837e01402b1SRalf Baechle 
838e01402b1SRalf Baechle 	for (i = 1; i < hdr->e_shnum; i++) {
839e01402b1SRalf Baechle 
840e01402b1SRalf Baechle 		if (sechdrs[i].sh_type != SHT_NOBITS
841e01402b1SRalf Baechle 		    && len < sechdrs[i].sh_offset + sechdrs[i].sh_size) {
842e01402b1SRalf Baechle 			printk(KERN_ERR "VPE program length %u truncated\n",
843e01402b1SRalf Baechle 			       len);
844e01402b1SRalf Baechle 			return -ENOEXEC;
845e01402b1SRalf Baechle 		}
846e01402b1SRalf Baechle 
847e01402b1SRalf Baechle 		/* Mark all sections sh_addr with their address in the
848e01402b1SRalf Baechle 		   temporary image. */
849e01402b1SRalf Baechle 		sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
850e01402b1SRalf Baechle 
851e01402b1SRalf Baechle 		/* Internal symbols and strings. */
852e01402b1SRalf Baechle 		if (sechdrs[i].sh_type == SHT_SYMTAB) {
853e01402b1SRalf Baechle 			symindex = i;
854e01402b1SRalf Baechle 			strindex = sechdrs[i].sh_link;
855e01402b1SRalf Baechle 			strtab = (char *)hdr + sechdrs[strindex].sh_offset;
856e01402b1SRalf Baechle 		}
857e01402b1SRalf Baechle 	}
858e01402b1SRalf Baechle 
859e01402b1SRalf Baechle 	layout_sections(&mod, hdr, sechdrs, secstrings);
860e01402b1SRalf Baechle 
861e01402b1SRalf Baechle 	v->load_addr = alloc_progmem(mod.core_size);
862e01402b1SRalf Baechle 	memset(v->load_addr, 0, mod.core_size);
863e01402b1SRalf Baechle 
864e01402b1SRalf Baechle 	printk("VPE elf_loader: loading to %p\n", v->load_addr);
865e01402b1SRalf Baechle 
866e01402b1SRalf Baechle 	for (i = 0; i < hdr->e_shnum; i++) {
867e01402b1SRalf Baechle 		void *dest;
868e01402b1SRalf Baechle 
869e01402b1SRalf Baechle 		if (!(sechdrs[i].sh_flags & SHF_ALLOC))
870e01402b1SRalf Baechle 			continue;
871e01402b1SRalf Baechle 
872e01402b1SRalf Baechle 		dest = v->load_addr + sechdrs[i].sh_entsize;
873e01402b1SRalf Baechle 
874e01402b1SRalf Baechle 		if (sechdrs[i].sh_type != SHT_NOBITS)
875e01402b1SRalf Baechle 			memcpy(dest, (void *)sechdrs[i].sh_addr,
876e01402b1SRalf Baechle 			       sechdrs[i].sh_size);
877e01402b1SRalf Baechle 		/* Update sh_addr to point to copy in image. */
878e01402b1SRalf Baechle 		sechdrs[i].sh_addr = (unsigned long)dest;
879e01402b1SRalf Baechle 	}
880e01402b1SRalf Baechle 
881e01402b1SRalf Baechle 	/* Fix up syms, so that st_value is a pointer to location. */
882e01402b1SRalf Baechle 	err =
883e01402b1SRalf Baechle 		simplify_symbols(sechdrs, symindex, strtab, secstrings,
884e01402b1SRalf Baechle 				 hdr->e_shnum, &mod);
885e01402b1SRalf Baechle 	if (err < 0) {
886e01402b1SRalf Baechle 		printk(KERN_WARNING "VPE: unable to simplify symbols\n");
887e01402b1SRalf Baechle 		goto cleanup;
888e01402b1SRalf Baechle 	}
889e01402b1SRalf Baechle 
890e01402b1SRalf Baechle 	/* Now do relocations. */
891e01402b1SRalf Baechle 	for (i = 1; i < hdr->e_shnum; i++) {
892e01402b1SRalf Baechle 		const char *strtab = (char *)sechdrs[strindex].sh_addr;
893e01402b1SRalf Baechle 		unsigned int info = sechdrs[i].sh_info;
894e01402b1SRalf Baechle 
895e01402b1SRalf Baechle 		/* Not a valid relocation section? */
896e01402b1SRalf Baechle 		if (info >= hdr->e_shnum)
897e01402b1SRalf Baechle 			continue;
898e01402b1SRalf Baechle 
899e01402b1SRalf Baechle 		/* Don't bother with non-allocated sections */
900e01402b1SRalf Baechle 		if (!(sechdrs[info].sh_flags & SHF_ALLOC))
901e01402b1SRalf Baechle 			continue;
902e01402b1SRalf Baechle 
903e01402b1SRalf Baechle 		if (sechdrs[i].sh_type == SHT_REL)
904e01402b1SRalf Baechle 			err =
905e01402b1SRalf Baechle 				apply_relocations(sechdrs, strtab, symindex, i, &mod);
906e01402b1SRalf Baechle 		else if (sechdrs[i].sh_type == SHT_RELA)
907e01402b1SRalf Baechle 			err = apply_relocate_add(sechdrs, strtab, symindex, i,
908e01402b1SRalf Baechle 						 &mod);
909e01402b1SRalf Baechle 		if (err < 0) {
910e01402b1SRalf Baechle 			printk(KERN_WARNING
911e01402b1SRalf Baechle 			       "vpe_elfload: error in relocations err %ld\n",
912e01402b1SRalf Baechle 			       err);
913e01402b1SRalf Baechle 			goto cleanup;
914e01402b1SRalf Baechle 		}
915e01402b1SRalf Baechle 	}
916e01402b1SRalf Baechle 
917e01402b1SRalf Baechle 	/* make sure it's physically written out */
918e01402b1SRalf Baechle 	flush_icache_range((unsigned long)v->load_addr,
919e01402b1SRalf Baechle 			   (unsigned long)v->load_addr + v->len);
920e01402b1SRalf Baechle 
921e01402b1SRalf Baechle 	if ((find_vpe_symbols(v, sechdrs, symindex, strtab, &mod)) < 0) {
922e01402b1SRalf Baechle 
923e01402b1SRalf Baechle 		printk(KERN_WARNING
924e01402b1SRalf Baechle 		       "VPE: program doesn't contain __start or vpe_shared symbols\n");
925e01402b1SRalf Baechle 		err = -ENOEXEC;
926e01402b1SRalf Baechle 	}
927e01402b1SRalf Baechle 
928e01402b1SRalf Baechle 	printk(" elf loaded\n");
929e01402b1SRalf Baechle 
930e01402b1SRalf Baechle cleanup:
931e01402b1SRalf Baechle 	return err;
932e01402b1SRalf Baechle }
933e01402b1SRalf Baechle 
934e01402b1SRalf Baechle static void dump_vpe(vpe_t * v)
935e01402b1SRalf Baechle {
936e01402b1SRalf Baechle 	struct tc *t;
937e01402b1SRalf Baechle 
938e01402b1SRalf Baechle 	printk(KERN_DEBUG "VPEControl 0x%lx\n", read_vpe_c0_vpecontrol());
939e01402b1SRalf Baechle 	printk(KERN_DEBUG "VPEConf0 0x%lx\n", read_vpe_c0_vpeconf0());
940e01402b1SRalf Baechle 
941e01402b1SRalf Baechle 	list_for_each_entry(t, &vpecontrol.tc_list, list) {
942e01402b1SRalf Baechle 		dump_tc(t);
943e01402b1SRalf Baechle 	}
944e01402b1SRalf Baechle }
945e01402b1SRalf Baechle 
946e01402b1SRalf Baechle /* checks for VPE is unused and gets ready to load program	 */
947e01402b1SRalf Baechle static int vpe_open(struct inode *inode, struct file *filp)
948e01402b1SRalf Baechle {
949e01402b1SRalf Baechle 	int minor;
950e01402b1SRalf Baechle 	vpe_t *v;
951e01402b1SRalf Baechle 
952e01402b1SRalf Baechle 	/* assume only 1 device at the mo. */
953e01402b1SRalf Baechle 	if ((minor = MINOR(inode->i_rdev)) != 1) {
954e01402b1SRalf Baechle 		printk(KERN_WARNING "VPE: only vpe1 is supported\n");
955e01402b1SRalf Baechle 		return -ENODEV;
956e01402b1SRalf Baechle 	}
957e01402b1SRalf Baechle 
958e01402b1SRalf Baechle 	if ((v = get_vpe(minor)) == NULL) {
959e01402b1SRalf Baechle 		printk(KERN_WARNING "VPE: unable to get vpe\n");
960e01402b1SRalf Baechle 		return -ENODEV;
961e01402b1SRalf Baechle 	}
962e01402b1SRalf Baechle 
963e01402b1SRalf Baechle 	if (v->state != VPE_STATE_UNUSED) {
964e01402b1SRalf Baechle 		unsigned long tmp;
965e01402b1SRalf Baechle 		struct tc *t;
966e01402b1SRalf Baechle 
967e01402b1SRalf Baechle 		printk(KERN_WARNING "VPE: device %d already in use\n", minor);
968e01402b1SRalf Baechle 
969e01402b1SRalf Baechle 		dvpe();
970e01402b1SRalf Baechle 		dump_vpe(v);
971e01402b1SRalf Baechle 
972e01402b1SRalf Baechle 		printk(KERN_WARNING "VPE: re-initialising %d\n", minor);
973e01402b1SRalf Baechle 
974e01402b1SRalf Baechle 		release_progmem(v->load_addr);
975e01402b1SRalf Baechle 
976e01402b1SRalf Baechle 		t = get_tc(minor);
977e01402b1SRalf Baechle 		settc(minor);
978e01402b1SRalf Baechle 		tmp = read_tc_c0_tcstatus();
979e01402b1SRalf Baechle 
980e01402b1SRalf Baechle 		/* mark not allocated and not dynamically allocatable */
981e01402b1SRalf Baechle 		tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
982e01402b1SRalf Baechle 		tmp |= TCSTATUS_IXMT;	/* interrupt exempt */
983e01402b1SRalf Baechle 		write_tc_c0_tcstatus(tmp);
984e01402b1SRalf Baechle 
985e01402b1SRalf Baechle 		write_tc_c0_tchalt(TCHALT_H);
986e01402b1SRalf Baechle 
987e01402b1SRalf Baechle 	}
988e01402b1SRalf Baechle 
989e01402b1SRalf Baechle 	// allocate it so when we get write ops we know it's expected.
990e01402b1SRalf Baechle 	v->state = VPE_STATE_INUSE;
991e01402b1SRalf Baechle 
992e01402b1SRalf Baechle 	/* this of-course trashes what was there before... */
993e01402b1SRalf Baechle 	v->pbuffer = vmalloc(P_SIZE);
994e01402b1SRalf Baechle 	v->plen = P_SIZE;
995e01402b1SRalf Baechle 	v->load_addr = NULL;
996e01402b1SRalf Baechle 	v->len = 0;
997e01402b1SRalf Baechle 
998e01402b1SRalf Baechle 	return 0;
999e01402b1SRalf Baechle }
1000e01402b1SRalf Baechle 
1001e01402b1SRalf Baechle static int vpe_release(struct inode *inode, struct file *filp)
1002e01402b1SRalf Baechle {
1003e01402b1SRalf Baechle 	int minor, ret = 0;
1004e01402b1SRalf Baechle 	vpe_t *v;
1005e01402b1SRalf Baechle 	Elf_Ehdr *hdr;
1006e01402b1SRalf Baechle 
1007e01402b1SRalf Baechle 	minor = MINOR(inode->i_rdev);
1008e01402b1SRalf Baechle 	if ((v = get_vpe(minor)) == NULL)
1009e01402b1SRalf Baechle 		return -ENODEV;
1010e01402b1SRalf Baechle 
1011e01402b1SRalf Baechle 	// simple case of fire and forget, so tell the VPE to run...
1012e01402b1SRalf Baechle 
1013e01402b1SRalf Baechle 	hdr = (Elf_Ehdr *) v->pbuffer;
1014e01402b1SRalf Baechle 	if (memcmp(hdr->e_ident, ELFMAG, 4) == 0) {
1015e01402b1SRalf Baechle 		if (vpe_elfload(v) >= 0)
1016e01402b1SRalf Baechle 			vpe_run(v);
1017e01402b1SRalf Baechle 		else {
1018e01402b1SRalf Baechle 			printk(KERN_WARNING "VPE: ELF load failed.\n");
1019e01402b1SRalf Baechle 			ret = -ENOEXEC;
1020e01402b1SRalf Baechle 		}
1021e01402b1SRalf Baechle 	} else {
1022e01402b1SRalf Baechle 		printk(KERN_WARNING "VPE: only elf files are supported\n");
1023e01402b1SRalf Baechle 		ret = -ENOEXEC;
1024e01402b1SRalf Baechle 	}
1025e01402b1SRalf Baechle 
1026e01402b1SRalf Baechle 	// cleanup any temp buffers
1027e01402b1SRalf Baechle 	if (v->pbuffer)
1028e01402b1SRalf Baechle 		vfree(v->pbuffer);
1029e01402b1SRalf Baechle 	v->plen = 0;
1030e01402b1SRalf Baechle 	return ret;
1031e01402b1SRalf Baechle }
1032e01402b1SRalf Baechle 
1033e01402b1SRalf Baechle static ssize_t vpe_write(struct file *file, const char __user * buffer,
1034e01402b1SRalf Baechle 			 size_t count, loff_t * ppos)
1035e01402b1SRalf Baechle {
1036e01402b1SRalf Baechle 	int minor;
1037e01402b1SRalf Baechle 	size_t ret = count;
1038e01402b1SRalf Baechle 	vpe_t *v;
1039e01402b1SRalf Baechle 
1040e01402b1SRalf Baechle 	minor = MINOR(file->f_dentry->d_inode->i_rdev);
1041e01402b1SRalf Baechle 	if ((v = get_vpe(minor)) == NULL)
1042e01402b1SRalf Baechle 		return -ENODEV;
1043e01402b1SRalf Baechle 
1044e01402b1SRalf Baechle 	if (v->pbuffer == NULL) {
1045e01402b1SRalf Baechle 		printk(KERN_ERR "vpe_write: no pbuffer\n");
1046e01402b1SRalf Baechle 		return -ENOMEM;
1047e01402b1SRalf Baechle 	}
1048e01402b1SRalf Baechle 
1049e01402b1SRalf Baechle 	if ((count + v->len) > v->plen) {
1050e01402b1SRalf Baechle 		printk(KERN_WARNING
1051e01402b1SRalf Baechle 		       "VPE Loader: elf size too big. Perhaps strip uneeded symbols\n");
1052e01402b1SRalf Baechle 		return -ENOMEM;
1053e01402b1SRalf Baechle 	}
1054e01402b1SRalf Baechle 
1055e01402b1SRalf Baechle 	count -= copy_from_user(v->pbuffer + v->len, buffer, count);
1056e01402b1SRalf Baechle 	if (!count) {
1057e01402b1SRalf Baechle 		printk("vpe_write: copy_to_user failed\n");
1058e01402b1SRalf Baechle 		return -EFAULT;
1059e01402b1SRalf Baechle 	}
1060e01402b1SRalf Baechle 
1061e01402b1SRalf Baechle 	v->len += count;
1062e01402b1SRalf Baechle 	return ret;
1063e01402b1SRalf Baechle }
1064e01402b1SRalf Baechle 
1065e01402b1SRalf Baechle static struct file_operations vpe_fops = {
1066e01402b1SRalf Baechle 	.owner = THIS_MODULE,
1067e01402b1SRalf Baechle 	.open = vpe_open,
1068e01402b1SRalf Baechle 	.release = vpe_release,
1069e01402b1SRalf Baechle 	.write = vpe_write
1070e01402b1SRalf Baechle };
1071e01402b1SRalf Baechle 
1072e01402b1SRalf Baechle /* module wrapper entry points */
1073e01402b1SRalf Baechle /* give me a vpe */
1074e01402b1SRalf Baechle vpe_handle vpe_alloc(void)
1075e01402b1SRalf Baechle {
1076e01402b1SRalf Baechle 	int i;
1077e01402b1SRalf Baechle 	struct vpe *v;
1078e01402b1SRalf Baechle 
1079e01402b1SRalf Baechle 	/* find a vpe */
1080e01402b1SRalf Baechle 	for (i = 1; i < MAX_VPES; i++) {
1081e01402b1SRalf Baechle 		if ((v = get_vpe(i)) != NULL) {
1082e01402b1SRalf Baechle 			v->state = VPE_STATE_INUSE;
1083e01402b1SRalf Baechle 			return v;
1084e01402b1SRalf Baechle 		}
1085e01402b1SRalf Baechle 	}
1086e01402b1SRalf Baechle 	return NULL;
1087e01402b1SRalf Baechle }
1088e01402b1SRalf Baechle 
1089e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_alloc);
1090e01402b1SRalf Baechle 
1091e01402b1SRalf Baechle /* start running from here */
1092e01402b1SRalf Baechle int vpe_start(vpe_handle vpe, unsigned long start)
1093e01402b1SRalf Baechle {
1094e01402b1SRalf Baechle 	struct vpe *v = vpe;
1095e01402b1SRalf Baechle 
1096e01402b1SRalf Baechle 	v->__start = start;
1097e01402b1SRalf Baechle 	return vpe_run(v);
1098e01402b1SRalf Baechle }
1099e01402b1SRalf Baechle 
1100e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_start);
1101e01402b1SRalf Baechle 
1102e01402b1SRalf Baechle /* halt it for now */
1103e01402b1SRalf Baechle int vpe_stop(vpe_handle vpe)
1104e01402b1SRalf Baechle {
1105e01402b1SRalf Baechle 	struct vpe *v = vpe;
1106e01402b1SRalf Baechle 	struct tc *t;
1107e01402b1SRalf Baechle 	unsigned int evpe_flags;
1108e01402b1SRalf Baechle 
1109e01402b1SRalf Baechle 	evpe_flags = dvpe();
1110e01402b1SRalf Baechle 
1111e01402b1SRalf Baechle 	if ((t = list_entry(v->tc.next, struct tc, tc)) != NULL) {
1112e01402b1SRalf Baechle 
1113e01402b1SRalf Baechle 		settc(t->index);
1114e01402b1SRalf Baechle 		write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
1115e01402b1SRalf Baechle 	}
1116e01402b1SRalf Baechle 
1117e01402b1SRalf Baechle 	evpe(evpe_flags);
1118e01402b1SRalf Baechle 
1119e01402b1SRalf Baechle 	return 0;
1120e01402b1SRalf Baechle }
1121e01402b1SRalf Baechle 
1122e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_stop);
1123e01402b1SRalf Baechle 
1124e01402b1SRalf Baechle /* I've done with it thank you */
1125e01402b1SRalf Baechle int vpe_free(vpe_handle vpe)
1126e01402b1SRalf Baechle {
1127e01402b1SRalf Baechle 	struct vpe *v = vpe;
1128e01402b1SRalf Baechle 	struct tc *t;
1129e01402b1SRalf Baechle 	unsigned int evpe_flags;
1130e01402b1SRalf Baechle 
1131e01402b1SRalf Baechle 	if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
1132e01402b1SRalf Baechle 		return -ENOEXEC;
1133e01402b1SRalf Baechle 	}
1134e01402b1SRalf Baechle 
1135e01402b1SRalf Baechle 	evpe_flags = dvpe();
1136e01402b1SRalf Baechle 
1137e01402b1SRalf Baechle 	/* Put MVPE's into 'configuration state' */
1138340ee4b9SRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1139e01402b1SRalf Baechle 
1140e01402b1SRalf Baechle 	settc(t->index);
1141e01402b1SRalf Baechle 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
1142e01402b1SRalf Baechle 
1143e01402b1SRalf Baechle 	/* mark the TC unallocated and halt'ed */
1144e01402b1SRalf Baechle 	write_tc_c0_tcstatus(read_tc_c0_tcstatus() & ~TCSTATUS_A);
1145e01402b1SRalf Baechle 	write_tc_c0_tchalt(TCHALT_H);
1146e01402b1SRalf Baechle 
1147e01402b1SRalf Baechle 	v->state = VPE_STATE_UNUSED;
1148e01402b1SRalf Baechle 
1149340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
1150e01402b1SRalf Baechle 	evpe(evpe_flags);
1151e01402b1SRalf Baechle 
1152e01402b1SRalf Baechle 	return 0;
1153e01402b1SRalf Baechle }
1154e01402b1SRalf Baechle 
1155e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_free);
1156e01402b1SRalf Baechle 
1157e01402b1SRalf Baechle void *vpe_get_shared(int index)
1158e01402b1SRalf Baechle {
1159e01402b1SRalf Baechle 	struct vpe *v;
1160e01402b1SRalf Baechle 
1161e01402b1SRalf Baechle 	if ((v = get_vpe(index)) == NULL) {
1162e01402b1SRalf Baechle 		printk(KERN_WARNING "vpe: invalid vpe index %d\n", index);
1163e01402b1SRalf Baechle 		return NULL;
1164e01402b1SRalf Baechle 	}
1165e01402b1SRalf Baechle 
1166e01402b1SRalf Baechle 	return v->shared_ptr;
1167e01402b1SRalf Baechle }
1168e01402b1SRalf Baechle 
1169e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_get_shared);
1170e01402b1SRalf Baechle 
1171e01402b1SRalf Baechle static int __init vpe_module_init(void)
1172e01402b1SRalf Baechle {
1173e01402b1SRalf Baechle 	struct vpe *v = NULL;
1174e01402b1SRalf Baechle 	struct tc *t;
1175e01402b1SRalf Baechle 	unsigned long val;
1176e01402b1SRalf Baechle 	int i;
1177e01402b1SRalf Baechle 
1178e01402b1SRalf Baechle 	if (!cpu_has_mipsmt) {
1179e01402b1SRalf Baechle 		printk("VPE loader: not a MIPS MT capable processor\n");
1180e01402b1SRalf Baechle 		return -ENODEV;
1181e01402b1SRalf Baechle 	}
1182e01402b1SRalf Baechle 
1183e01402b1SRalf Baechle 	if ((major = register_chrdev(VPE_MAJOR, module_name, &vpe_fops) < 0)) {
1184e01402b1SRalf Baechle 		printk("VPE loader: unable to register character device\n");
1185e01402b1SRalf Baechle 		return -EBUSY;
1186e01402b1SRalf Baechle 	}
1187e01402b1SRalf Baechle 
1188e01402b1SRalf Baechle 	if (major == 0)
1189e01402b1SRalf Baechle 		major = VPE_MAJOR;
1190e01402b1SRalf Baechle 
1191e01402b1SRalf Baechle 	dmt();
1192e01402b1SRalf Baechle 	dvpe();
1193e01402b1SRalf Baechle 
1194e01402b1SRalf Baechle 	/* Put MVPE's into 'configuration state' */
1195340ee4b9SRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1196e01402b1SRalf Baechle 
1197e01402b1SRalf Baechle 	/* dump_mtregs(); */
1198e01402b1SRalf Baechle 
1199e01402b1SRalf Baechle 	INIT_LIST_HEAD(&vpecontrol.vpe_list);
1200e01402b1SRalf Baechle 	INIT_LIST_HEAD(&vpecontrol.tc_list);
1201e01402b1SRalf Baechle 
1202e01402b1SRalf Baechle 	val = read_c0_mvpconf0();
1203e01402b1SRalf Baechle 	for (i = 0; i < ((val & MVPCONF0_PTC) + 1); i++) {
1204e01402b1SRalf Baechle 		t = alloc_tc(i);
1205e01402b1SRalf Baechle 
1206e01402b1SRalf Baechle 		/* VPE's */
1207e01402b1SRalf Baechle 		if (i < ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1) {
1208e01402b1SRalf Baechle 			settc(i);
1209e01402b1SRalf Baechle 
1210e01402b1SRalf Baechle 			if ((v = alloc_vpe(i)) == NULL) {
1211e01402b1SRalf Baechle 				printk(KERN_WARNING "VPE: unable to allocate VPE\n");
1212e01402b1SRalf Baechle 				return -ENODEV;
1213e01402b1SRalf Baechle 			}
1214e01402b1SRalf Baechle 
1215e01402b1SRalf Baechle 			list_add(&t->tc, &v->tc);	/* add the tc to the list of this vpe's tc's. */
1216e01402b1SRalf Baechle 
1217e01402b1SRalf Baechle 			/* deactivate all but vpe0 */
1218e01402b1SRalf Baechle 			if (i != 0) {
1219e01402b1SRalf Baechle 				unsigned long tmp = read_vpe_c0_vpeconf0();
1220e01402b1SRalf Baechle 
1221e01402b1SRalf Baechle 				tmp &= ~VPECONF0_VPA;
1222e01402b1SRalf Baechle 
1223e01402b1SRalf Baechle 				/* master VPE */
1224e01402b1SRalf Baechle 				tmp |= VPECONF0_MVP;
1225e01402b1SRalf Baechle 				write_vpe_c0_vpeconf0(tmp);
1226e01402b1SRalf Baechle 			}
1227e01402b1SRalf Baechle 
1228e01402b1SRalf Baechle 			/* disable multi-threading with TC's */
1229e01402b1SRalf Baechle 			write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE);
1230e01402b1SRalf Baechle 
1231e01402b1SRalf Baechle 			if (i != 0) {
1232e01402b1SRalf Baechle 				write_vpe_c0_status((read_c0_status() &
1233e01402b1SRalf Baechle 						     ~(ST0_IM | ST0_IE | ST0_KSU))
1234e01402b1SRalf Baechle 						    | ST0_CU0);
1235e01402b1SRalf Baechle 
1236e01402b1SRalf Baechle 				/* set config to be the same as vpe0, particularly kseg0 coherency alg */
1237e01402b1SRalf Baechle 				write_vpe_c0_config(read_c0_config());
1238e01402b1SRalf Baechle 			}
1239e01402b1SRalf Baechle 
1240e01402b1SRalf Baechle 		}
1241e01402b1SRalf Baechle 
1242e01402b1SRalf Baechle 		/* TC's */
1243e01402b1SRalf Baechle 		t->pvpe = v;	/* set the parent vpe */
1244e01402b1SRalf Baechle 
1245e01402b1SRalf Baechle 		if (i != 0) {
1246e01402b1SRalf Baechle 			unsigned long tmp;
1247e01402b1SRalf Baechle 
1248e01402b1SRalf Baechle 			/* tc 0 will of course be running.... */
1249e01402b1SRalf Baechle 			if (i == 0)
1250e01402b1SRalf Baechle 				t->state = TC_STATE_RUNNING;
1251e01402b1SRalf Baechle 
1252e01402b1SRalf Baechle 			settc(i);
1253e01402b1SRalf Baechle 
1254e01402b1SRalf Baechle 			/* bind a TC to each VPE, May as well put all excess TC's
1255e01402b1SRalf Baechle 			   on the last VPE */
1256e01402b1SRalf Baechle 			if (i >= (((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1))
1257e01402b1SRalf Baechle 				write_tc_c0_tcbind(read_tc_c0_tcbind() |
1258e01402b1SRalf Baechle 						   ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT));
1259e01402b1SRalf Baechle 			else
1260e01402b1SRalf Baechle 				write_tc_c0_tcbind(read_tc_c0_tcbind() | i);
1261e01402b1SRalf Baechle 
1262e01402b1SRalf Baechle 			tmp = read_tc_c0_tcstatus();
1263e01402b1SRalf Baechle 
1264e01402b1SRalf Baechle 			/* mark not allocated and not dynamically allocatable */
1265e01402b1SRalf Baechle 			tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1266e01402b1SRalf Baechle 			tmp |= TCSTATUS_IXMT;	/* interrupt exempt */
1267e01402b1SRalf Baechle 			write_tc_c0_tcstatus(tmp);
1268e01402b1SRalf Baechle 
1269e01402b1SRalf Baechle 			write_tc_c0_tchalt(TCHALT_H);
1270e01402b1SRalf Baechle 		}
1271e01402b1SRalf Baechle 	}
1272e01402b1SRalf Baechle 
1273e01402b1SRalf Baechle 	/* release config state */
1274340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
1275e01402b1SRalf Baechle 
1276e01402b1SRalf Baechle 	return 0;
1277e01402b1SRalf Baechle }
1278e01402b1SRalf Baechle 
1279e01402b1SRalf Baechle static void __exit vpe_module_exit(void)
1280e01402b1SRalf Baechle {
1281e01402b1SRalf Baechle 	struct vpe *v, *n;
1282e01402b1SRalf Baechle 
1283e01402b1SRalf Baechle 	list_for_each_entry_safe(v, n, &vpecontrol.vpe_list, list) {
1284e01402b1SRalf Baechle 		if (v->state != VPE_STATE_UNUSED) {
1285e01402b1SRalf Baechle 			release_vpe(v);
1286e01402b1SRalf Baechle 		}
1287e01402b1SRalf Baechle 	}
1288e01402b1SRalf Baechle 
1289e01402b1SRalf Baechle 	unregister_chrdev(major, module_name);
1290e01402b1SRalf Baechle }
1291e01402b1SRalf Baechle 
1292e01402b1SRalf Baechle module_init(vpe_module_init);
1293e01402b1SRalf Baechle module_exit(vpe_module_exit);
1294e01402b1SRalf Baechle MODULE_DESCRIPTION("MIPS VPE Loader");
1295e01402b1SRalf Baechle MODULE_AUTHOR("Elizabeth Clarke, MIPS Technologies, Inc");
1296e01402b1SRalf Baechle MODULE_LICENSE("GPL");
1297