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