]> git.ipfire.org Git - people/ms/linux.git/blame - arch/mips/kernel/vpe.c
atomic: use <linux/atomic.h>
[people/ms/linux.git] / arch / mips / kernel / vpe.c
CommitLineData
e01402b1
RB
1/*
2 * Copyright (C) 2004, 2005 MIPS Technologies, Inc. All rights reserved.
3 *
4 * This program is free software; you can distribute it and/or modify it
5 * under the terms of the GNU General Public License (Version 2) as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
e01402b1
RB
16 */
17
18/*
19 * VPE support module
20 *
21 * Provides support for loading a MIPS SP program on VPE1.
25985edc 22 * The SP environment is rather simple, no tlb's. It needs to be relocatable
e01402b1
RB
23 * (or partially linked). You should initialise your stack in the startup
24 * code. This loader looks for the symbol __start and sets up
25 * execution to resume from there. The MIPS SDE kit contains suitable examples.
26 *
27 * To load and run, simply cat a SP 'program file' to /dev/vpe1.
28 * i.e cat spapp >/dev/vpe1.
e01402b1 29 */
e01402b1 30#include <linux/kernel.h>
27a3bbaf 31#include <linux/device.h>
e01402b1
RB
32#include <linux/module.h>
33#include <linux/fs.h>
34#include <linux/init.h>
35#include <asm/uaccess.h>
36#include <linux/slab.h>
37#include <linux/list.h>
38#include <linux/vmalloc.h>
39#include <linux/elf.h>
40#include <linux/seq_file.h>
41#include <linux/syscalls.h>
42#include <linux/moduleloader.h>
43#include <linux/interrupt.h>
44#include <linux/poll.h>
45#include <linux/bootmem.h>
46#include <asm/mipsregs.h>
340ee4b9 47#include <asm/mipsmtregs.h>
e01402b1 48#include <asm/cacheflush.h>
60063497 49#include <linux/atomic.h>
e01402b1 50#include <asm/cpu.h>
27a3bbaf 51#include <asm/mips_mt.h>
e01402b1
RB
52#include <asm/processor.h>
53#include <asm/system.h>
2600990e
RB
54#include <asm/vpe.h>
55#include <asm/kspd.h>
e01402b1
RB
56
57typedef void *vpe_handle;
58
e01402b1
RB
59#ifndef ARCH_SHF_SMALL
60#define ARCH_SHF_SMALL 0
61#endif
62
63/* If this is set, the section belongs in the init part of the module */
64#define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
65
41790e04
RB
66/*
67 * The number of TCs and VPEs physically available on the core
68 */
69static int hw_tcs, hw_vpes;
e01402b1 70static char module_name[] = "vpe";
307bd284 71static int major;
27a3bbaf 72static const int minor = 1; /* fixed for now */
e01402b1 73
2600990e 74#ifdef CONFIG_MIPS_APSP_KSPD
349c4229 75static struct kspd_notifications kspd_events;
982f6ffe 76static int kspd_events_reqd;
2600990e
RB
77#endif
78
e01402b1
RB
79/* grab the likely amount of memory we will need. */
80#ifdef CONFIG_MIPS_VPE_LOADER_TOM
81#define P_SIZE (2 * 1024 * 1024)
82#else
83/* add an overhead to the max kmalloc size for non-striped symbols/etc */
84#define P_SIZE (256 * 1024)
85#endif
86
2600990e
RB
87extern unsigned long physical_memsize;
88
e01402b1 89#define MAX_VPES 16
2600990e 90#define VPE_PATH_MAX 256
e01402b1
RB
91
92enum vpe_state {
93 VPE_STATE_UNUSED = 0,
94 VPE_STATE_INUSE,
95 VPE_STATE_RUNNING
96};
97
98enum tc_state {
99 TC_STATE_UNUSED = 0,
100 TC_STATE_INUSE,
101 TC_STATE_RUNNING,
102 TC_STATE_DYNAMIC
103};
104
307bd284 105struct vpe {
e01402b1
RB
106 enum vpe_state state;
107
108 /* (device) minor associated with this vpe */
109 int minor;
110
111 /* elfloader stuff */
112 void *load_addr;
571e0bed 113 unsigned long len;
e01402b1 114 char *pbuffer;
571e0bed 115 unsigned long plen;
2600990e
RB
116 unsigned int uid, gid;
117 char cwd[VPE_PATH_MAX];
e01402b1
RB
118
119 unsigned long __start;
120
121 /* tc's associated with this vpe */
122 struct list_head tc;
123
124 /* The list of vpe's */
125 struct list_head list;
126
127 /* shared symbol address */
128 void *shared_ptr;
2600990e
RB
129
130 /* the list of who wants to know when something major happens */
131 struct list_head notify;
41790e04
RB
132
133 unsigned int ntcs;
307bd284
RB
134};
135
136struct tc {
137 enum tc_state state;
138 int index;
139
07cc0c9e
RB
140 struct vpe *pvpe; /* parent VPE */
141 struct list_head tc; /* The list of TC's with this VPE */
142 struct list_head list; /* The global list of tc's */
307bd284 143};
e01402b1 144
9cfdf6f1 145struct {
1bbfc20d
RB
146 spinlock_t vpe_list_lock;
147 struct list_head vpe_list; /* Virtual processing elements */
148 spinlock_t tc_list_lock;
149 struct list_head tc_list; /* Thread contexts */
9cfdf6f1 150} vpecontrol = {
52bd080d 151 .vpe_list_lock = __SPIN_LOCK_UNLOCKED(vpe_list_lock),
1bbfc20d 152 .vpe_list = LIST_HEAD_INIT(vpecontrol.vpe_list),
52bd080d 153 .tc_list_lock = __SPIN_LOCK_UNLOCKED(tc_list_lock),
1bbfc20d 154 .tc_list = LIST_HEAD_INIT(vpecontrol.tc_list)
9cfdf6f1 155};
e01402b1
RB
156
157static void release_progmem(void *ptr);
e01402b1
RB
158
159/* get the vpe associated with this minor */
f18b51cc 160static struct vpe *get_vpe(int minor)
e01402b1 161{
1bbfc20d 162 struct vpe *res, *v;
e01402b1 163
2600990e
RB
164 if (!cpu_has_mipsmt)
165 return NULL;
166
1bbfc20d
RB
167 res = NULL;
168 spin_lock(&vpecontrol.vpe_list_lock);
e01402b1 169 list_for_each_entry(v, &vpecontrol.vpe_list, list) {
1bbfc20d
RB
170 if (v->minor == minor) {
171 res = v;
172 break;
173 }
e01402b1 174 }
1bbfc20d 175 spin_unlock(&vpecontrol.vpe_list_lock);
e01402b1 176
1bbfc20d 177 return res;
e01402b1
RB
178}
179
180/* get the vpe associated with this minor */
f18b51cc 181static struct tc *get_tc(int index)
e01402b1 182{
1bbfc20d 183 struct tc *res, *t;
e01402b1 184
1bbfc20d
RB
185 res = NULL;
186 spin_lock(&vpecontrol.tc_list_lock);
e01402b1 187 list_for_each_entry(t, &vpecontrol.tc_list, list) {
1bbfc20d
RB
188 if (t->index == index) {
189 res = t;
190 break;
191 }
e01402b1 192 }
1bbfc20d 193 spin_unlock(&vpecontrol.tc_list_lock);
e01402b1 194
e01402b1
RB
195 return NULL;
196}
197
e01402b1 198/* allocate a vpe and associate it with this minor (or index) */
f18b51cc 199static struct vpe *alloc_vpe(int minor)
e01402b1
RB
200{
201 struct vpe *v;
202
1bbfc20d 203 if ((v = kzalloc(sizeof(struct vpe), GFP_KERNEL)) == NULL)
e01402b1 204 return NULL;
e01402b1 205
e01402b1 206 INIT_LIST_HEAD(&v->tc);
1bbfc20d 207 spin_lock(&vpecontrol.vpe_list_lock);
e01402b1 208 list_add_tail(&v->list, &vpecontrol.vpe_list);
1bbfc20d 209 spin_unlock(&vpecontrol.vpe_list_lock);
e01402b1 210
2600990e 211 INIT_LIST_HEAD(&v->notify);
e01402b1 212 v->minor = minor;
1bbfc20d 213
e01402b1
RB
214 return v;
215}
216
217/* allocate a tc. At startup only tc0 is running, all other can be halted. */
f18b51cc 218static struct tc *alloc_tc(int index)
e01402b1 219{
07cc0c9e 220 struct tc *tc;
e01402b1 221
07cc0c9e
RB
222 if ((tc = kzalloc(sizeof(struct tc), GFP_KERNEL)) == NULL)
223 goto out;
e01402b1 224
07cc0c9e
RB
225 INIT_LIST_HEAD(&tc->tc);
226 tc->index = index;
1bbfc20d
RB
227
228 spin_lock(&vpecontrol.tc_list_lock);
07cc0c9e 229 list_add_tail(&tc->list, &vpecontrol.tc_list);
1bbfc20d 230 spin_unlock(&vpecontrol.tc_list_lock);
e01402b1 231
07cc0c9e
RB
232out:
233 return tc;
e01402b1
RB
234}
235
236/* clean up and free everything */
f18b51cc 237static void release_vpe(struct vpe *v)
e01402b1
RB
238{
239 list_del(&v->list);
240 if (v->load_addr)
241 release_progmem(v);
242 kfree(v);
243}
244
1bbfc20d 245static void __maybe_unused dump_mtregs(void)
e01402b1
RB
246{
247 unsigned long val;
248
249 val = read_c0_config3();
250 printk("config3 0x%lx MT %ld\n", val,
251 (val & CONFIG3_MT) >> CONFIG3_MT_SHIFT);
252
e01402b1
RB
253 val = read_c0_mvpcontrol();
254 printk("MVPControl 0x%lx, STLB %ld VPC %ld EVP %ld\n", val,
255 (val & MVPCONTROL_STLB) >> MVPCONTROL_STLB_SHIFT,
256 (val & MVPCONTROL_VPC) >> MVPCONTROL_VPC_SHIFT,
257 (val & MVPCONTROL_EVP));
258
2600990e
RB
259 val = read_c0_mvpconf0();
260 printk("mvpconf0 0x%lx, PVPE %ld PTC %ld M %ld\n", val,
261 (val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT,
262 val & MVPCONF0_PTC, (val & MVPCONF0_M) >> MVPCONF0_M_SHIFT);
e01402b1
RB
263}
264
265/* Find some VPE program space */
571e0bed 266static void *alloc_progmem(unsigned long len)
e01402b1 267{
5408c490
RB
268 void *addr;
269
e01402b1 270#ifdef CONFIG_MIPS_VPE_LOADER_TOM
5408c490
RB
271 /*
272 * This means you must tell Linux to use less memory than you
273 * physically have, for example by passing a mem= boot argument.
274 */
9f2546ad 275 addr = pfn_to_kaddr(max_low_pfn);
5408c490 276 memset(addr, 0, len);
e01402b1 277#else
5408c490
RB
278 /* simple grab some mem for now */
279 addr = kzalloc(len, GFP_KERNEL);
e01402b1 280#endif
5408c490
RB
281
282 return addr;
e01402b1
RB
283}
284
285static void release_progmem(void *ptr)
286{
287#ifndef CONFIG_MIPS_VPE_LOADER_TOM
288 kfree(ptr);
289#endif
290}
291
292/* Update size with this section: return offset. */
293static long get_offset(unsigned long *size, Elf_Shdr * sechdr)
294{
295 long ret;
296
297 ret = ALIGN(*size, sechdr->sh_addralign ? : 1);
298 *size = ret + sechdr->sh_size;
299 return ret;
300}
301
302/* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld
303 might -- code, read-only data, read-write data, small data. Tally
304 sizes, and place the offsets into sh_entsize fields: high bit means it
305 belongs in init. */
306static void layout_sections(struct module *mod, const Elf_Ehdr * hdr,
307 Elf_Shdr * sechdrs, const char *secstrings)
308{
309 static unsigned long const masks[][2] = {
310 /* NOTE: all executable code must be the first section
311 * in this array; otherwise modify the text_size
312 * finder in the two loops below */
313 {SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL},
314 {SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL},
315 {SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL},
316 {ARCH_SHF_SMALL | SHF_ALLOC, 0}
317 };
318 unsigned int m, i;
319
320 for (i = 0; i < hdr->e_shnum; i++)
321 sechdrs[i].sh_entsize = ~0UL;
322
323 for (m = 0; m < ARRAY_SIZE(masks); ++m) {
324 for (i = 0; i < hdr->e_shnum; ++i) {
325 Elf_Shdr *s = &sechdrs[i];
326
327 // || strncmp(secstrings + s->sh_name, ".init", 5) == 0)
328 if ((s->sh_flags & masks[m][0]) != masks[m][0]
329 || (s->sh_flags & masks[m][1])
330 || s->sh_entsize != ~0UL)
331 continue;
e2a9cf96
RG
332 s->sh_entsize =
333 get_offset((unsigned long *)&mod->core_size, s);
e01402b1
RB
334 }
335
336 if (m == 0)
337 mod->core_text_size = mod->core_size;
338
339 }
340}
341
342
343/* from module-elf32.c, but subverted a little */
344
345struct mips_hi16 {
346 struct mips_hi16 *next;
347 Elf32_Addr *addr;
348 Elf32_Addr value;
349};
350
351static struct mips_hi16 *mips_hi16_list;
352static unsigned int gp_offs, gp_addr;
353
354static int apply_r_mips_none(struct module *me, uint32_t *location,
355 Elf32_Addr v)
356{
357 return 0;
358}
359
360static int apply_r_mips_gprel16(struct module *me, uint32_t *location,
361 Elf32_Addr v)
362{
363 int rel;
364
365 if( !(*location & 0xffff) ) {
366 rel = (int)v - gp_addr;
367 }
368 else {
369 /* .sbss + gp(relative) + offset */
370 /* kludge! */
371 rel = (int)(short)((int)v + gp_offs +
372 (int)(short)(*location & 0xffff) - gp_addr);
373 }
374
375 if( (rel > 32768) || (rel < -32768) ) {
2600990e
RB
376 printk(KERN_DEBUG "VPE loader: apply_r_mips_gprel16: "
377 "relative address 0x%x out of range of gp register\n",
378 rel);
e01402b1
RB
379 return -ENOEXEC;
380 }
381
382 *location = (*location & 0xffff0000) | (rel & 0xffff);
383
384 return 0;
385}
386
387static int apply_r_mips_pc16(struct module *me, uint32_t *location,
388 Elf32_Addr v)
389{
390 int rel;
391 rel = (((unsigned int)v - (unsigned int)location));
392 rel >>= 2; // because the offset is in _instructions_ not bytes.
393 rel -= 1; // and one instruction less due to the branch delay slot.
394
395 if( (rel > 32768) || (rel < -32768) ) {
2600990e
RB
396 printk(KERN_DEBUG "VPE loader: "
397 "apply_r_mips_pc16: relative address out of range 0x%x\n", rel);
e01402b1
RB
398 return -ENOEXEC;
399 }
400
401 *location = (*location & 0xffff0000) | (rel & 0xffff);
402
403 return 0;
404}
405
406static int apply_r_mips_32(struct module *me, uint32_t *location,
407 Elf32_Addr v)
408{
409 *location += v;
410
411 return 0;
412}
413
414static int apply_r_mips_26(struct module *me, uint32_t *location,
415 Elf32_Addr v)
416{
417 if (v % 4) {
2600990e
RB
418 printk(KERN_DEBUG "VPE loader: apply_r_mips_26 "
419 " unaligned relocation\n");
e01402b1
RB
420 return -ENOEXEC;
421 }
422
307bd284
RB
423/*
424 * Not desperately convinced this is a good check of an overflow condition
425 * anyway. But it gets in the way of handling undefined weak symbols which
426 * we want to set to zero.
427 * if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) {
428 * printk(KERN_ERR
429 * "module %s: relocation overflow\n",
430 * me->name);
431 * return -ENOEXEC;
432 * }
433 */
e01402b1
RB
434
435 *location = (*location & ~0x03ffffff) |
436 ((*location + (v >> 2)) & 0x03ffffff);
437 return 0;
438}
439
440static int apply_r_mips_hi16(struct module *me, uint32_t *location,
441 Elf32_Addr v)
442{
443 struct mips_hi16 *n;
444
445 /*
446 * We cannot relocate this one now because we don't know the value of
447 * the carry we need to add. Save the information, and let LO16 do the
448 * actual relocation.
449 */
450 n = kmalloc(sizeof *n, GFP_KERNEL);
451 if (!n)
452 return -ENOMEM;
453
454 n->addr = location;
455 n->value = v;
456 n->next = mips_hi16_list;
457 mips_hi16_list = n;
458
459 return 0;
460}
461
462static int apply_r_mips_lo16(struct module *me, uint32_t *location,
463 Elf32_Addr v)
464{
465 unsigned long insnlo = *location;
466 Elf32_Addr val, vallo;
477c4b07 467 struct mips_hi16 *l, *next;
e01402b1
RB
468
469 /* Sign extend the addend we extract from the lo insn. */
470 vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;
471
472 if (mips_hi16_list != NULL) {
e01402b1
RB
473
474 l = mips_hi16_list;
475 while (l != NULL) {
e01402b1
RB
476 unsigned long insn;
477
478 /*
479 * The value for the HI16 had best be the same.
480 */
2600990e
RB
481 if (v != l->value) {
482 printk(KERN_DEBUG "VPE loader: "
b1e3afa0 483 "apply_r_mips_lo16/hi16: \t"
2600990e 484 "inconsistent value information\n");
477c4b07 485 goto out_free;
e01402b1
RB
486 }
487
e01402b1
RB
488 /*
489 * Do the HI16 relocation. Note that we actually don't
490 * need to know anything about the LO16 itself, except
491 * where to find the low 16 bits of the addend needed
492 * by the LO16.
493 */
494 insn = *l->addr;
495 val = ((insn & 0xffff) << 16) + vallo;
496 val += v;
497
498 /*
499 * Account for the sign extension that will happen in
500 * the low bits.
501 */
502 val = ((val >> 16) + ((val & 0x8000) != 0)) & 0xffff;
503
504 insn = (insn & ~0xffff) | val;
505 *l->addr = insn;
506
507 next = l->next;
508 kfree(l);
509 l = next;
510 }
511
512 mips_hi16_list = NULL;
513 }
514
515 /*
516 * Ok, we're done with the HI16 relocs. Now deal with the LO16.
517 */
518 val = v + vallo;
519 insnlo = (insnlo & ~0xffff) | (val & 0xffff);
520 *location = insnlo;
521
522 return 0;
477c4b07
RB
523
524out_free:
525 while (l != NULL) {
526 next = l->next;
527 kfree(l);
528 l = next;
529 }
530 mips_hi16_list = NULL;
531
532 return -ENOEXEC;
e01402b1
RB
533}
534
535static int (*reloc_handlers[]) (struct module *me, uint32_t *location,
536 Elf32_Addr v) = {
537 [R_MIPS_NONE] = apply_r_mips_none,
538 [R_MIPS_32] = apply_r_mips_32,
539 [R_MIPS_26] = apply_r_mips_26,
540 [R_MIPS_HI16] = apply_r_mips_hi16,
541 [R_MIPS_LO16] = apply_r_mips_lo16,
542 [R_MIPS_GPREL16] = apply_r_mips_gprel16,
543 [R_MIPS_PC16] = apply_r_mips_pc16
544};
545
2600990e 546static char *rstrs[] = {
e0daad44 547 [R_MIPS_NONE] = "MIPS_NONE",
2600990e
RB
548 [R_MIPS_32] = "MIPS_32",
549 [R_MIPS_26] = "MIPS_26",
550 [R_MIPS_HI16] = "MIPS_HI16",
551 [R_MIPS_LO16] = "MIPS_LO16",
552 [R_MIPS_GPREL16] = "MIPS_GPREL16",
553 [R_MIPS_PC16] = "MIPS_PC16"
554};
e01402b1 555
f18b51cc 556static int apply_relocations(Elf32_Shdr *sechdrs,
e01402b1
RB
557 const char *strtab,
558 unsigned int symindex,
559 unsigned int relsec,
560 struct module *me)
561{
562 Elf32_Rel *rel = (void *) sechdrs[relsec].sh_addr;
563 Elf32_Sym *sym;
564 uint32_t *location;
565 unsigned int i;
566 Elf32_Addr v;
567 int res;
568
569 for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
570 Elf32_Word r_info = rel[i].r_info;
571
572 /* This is where to make the change */
573 location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
574 + rel[i].r_offset;
575 /* This is the symbol it is referring to */
576 sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
577 + ELF32_R_SYM(r_info);
578
579 if (!sym->st_value) {
580 printk(KERN_DEBUG "%s: undefined weak symbol %s\n",
581 me->name, strtab + sym->st_name);
582 /* just print the warning, dont barf */
583 }
584
585 v = sym->st_value;
586
587 res = reloc_handlers[ELF32_R_TYPE(r_info)](me, location, v);
588 if( res ) {
2600990e
RB
589 char *r = rstrs[ELF32_R_TYPE(r_info)];
590 printk(KERN_WARNING "VPE loader: .text+0x%x "
591 "relocation type %s for symbol \"%s\" failed\n",
592 rel[i].r_offset, r ? r : "UNKNOWN",
593 strtab + sym->st_name);
e01402b1 594 return res;
2600990e 595 }
e01402b1
RB
596 }
597
598 return 0;
599}
600
f18b51cc 601static inline void save_gp_address(unsigned int secbase, unsigned int rel)
e01402b1
RB
602{
603 gp_addr = secbase + rel;
604 gp_offs = gp_addr - (secbase & 0xffff0000);
605}
606/* end module-elf32.c */
607
608
609
610/* Change all symbols so that sh_value encodes the pointer directly. */
2600990e 611static void simplify_symbols(Elf_Shdr * sechdrs,
e01402b1
RB
612 unsigned int symindex,
613 const char *strtab,
614 const char *secstrings,
615 unsigned int nsecs, struct module *mod)
616{
617 Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
618 unsigned long secbase, bssbase = 0;
619 unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
2600990e 620 int size;
e01402b1
RB
621
622 /* find the .bss section for COMMON symbols */
623 for (i = 0; i < nsecs; i++) {
2600990e 624 if (strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) == 0) {
e01402b1 625 bssbase = sechdrs[i].sh_addr;
2600990e
RB
626 break;
627 }
e01402b1
RB
628 }
629
630 for (i = 1; i < n; i++) {
631 switch (sym[i].st_shndx) {
632 case SHN_COMMON:
2600990e
RB
633 /* Allocate space for the symbol in the .bss section.
634 st_value is currently size.
e01402b1
RB
635 We want it to have the address of the symbol. */
636
637 size = sym[i].st_value;
638 sym[i].st_value = bssbase;
639
640 bssbase += size;
641 break;
642
643 case SHN_ABS:
644 /* Don't need to do anything */
645 break;
646
647 case SHN_UNDEF:
648 /* ret = -ENOENT; */
649 break;
650
651 case SHN_MIPS_SCOMMON:
b1e3afa0 652 printk(KERN_DEBUG "simplify_symbols: ignoring SHN_MIPS_SCOMMON "
2600990e
RB
653 "symbol <%s> st_shndx %d\n", strtab + sym[i].st_name,
654 sym[i].st_shndx);
e01402b1
RB
655 // .sbss section
656 break;
657
658 default:
659 secbase = sechdrs[sym[i].st_shndx].sh_addr;
660
661 if (strncmp(strtab + sym[i].st_name, "_gp", 3) == 0) {
662 save_gp_address(secbase, sym[i].st_value);
663 }
664
665 sym[i].st_value += secbase;
666 break;
667 }
e01402b1 668 }
e01402b1
RB
669}
670
671#ifdef DEBUG_ELFLOADER
672static void dump_elfsymbols(Elf_Shdr * sechdrs, unsigned int symindex,
673 const char *strtab, struct module *mod)
674{
675 Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
676 unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
677
678 printk(KERN_DEBUG "dump_elfsymbols: n %d\n", n);
679 for (i = 1; i < n; i++) {
680 printk(KERN_DEBUG " i %d name <%s> 0x%x\n", i,
681 strtab + sym[i].st_name, sym[i].st_value);
682 }
683}
684#endif
685
e01402b1 686/* We are prepared so configure and start the VPE... */
be6e1437 687static int vpe_run(struct vpe * v)
e01402b1 688{
07cc0c9e 689 unsigned long flags, val, dmt_flag;
2600990e 690 struct vpe_notifications *n;
07cc0c9e 691 unsigned int vpeflags;
e01402b1
RB
692 struct tc *t;
693
694 /* check we are the Master VPE */
07cc0c9e 695 local_irq_save(flags);
e01402b1
RB
696 val = read_c0_vpeconf0();
697 if (!(val & VPECONF0_MVP)) {
698 printk(KERN_WARNING
2600990e 699 "VPE loader: only Master VPE's are allowed to configure MT\n");
07cc0c9e
RB
700 local_irq_restore(flags);
701
e01402b1
RB
702 return -1;
703 }
704
07cc0c9e
RB
705 dmt_flag = dmt();
706 vpeflags = dvpe();
e01402b1 707
2600990e 708 if (!list_empty(&v->tc)) {
e0daad44 709 if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
07cc0c9e
RB
710 evpe(vpeflags);
711 emt(dmt_flag);
712 local_irq_restore(flags);
713
714 printk(KERN_WARNING
715 "VPE loader: TC %d is already in use.\n",
716 t->index);
e0daad44
RB
717 return -ENOEXEC;
718 }
719 } else {
07cc0c9e
RB
720 evpe(vpeflags);
721 emt(dmt_flag);
722 local_irq_restore(flags);
723
724 printk(KERN_WARNING
725 "VPE loader: No TC's associated with VPE %d\n",
e0daad44 726 v->minor);
07cc0c9e 727
e0daad44
RB
728 return -ENOEXEC;
729 }
2600990e 730
e01402b1 731 /* Put MVPE's into 'configuration state' */
340ee4b9 732 set_c0_mvpcontrol(MVPCONTROL_VPC);
e01402b1 733
e01402b1
RB
734 settc(t->index);
735
e01402b1
RB
736 /* should check it is halted, and not activated */
737 if ((read_tc_c0_tcstatus() & TCSTATUS_A) || !(read_tc_c0_tchalt() & TCHALT_H)) {
07cc0c9e
RB
738 evpe(vpeflags);
739 emt(dmt_flag);
740 local_irq_restore(flags);
741
742 printk(KERN_WARNING "VPE loader: TC %d is already active!\n",
e01402b1 743 t->index);
07cc0c9e 744
e01402b1
RB
745 return -ENOEXEC;
746 }
747
748 /* Write the address we want it to start running from in the TCPC register. */
749 write_tc_c0_tcrestart((unsigned long)v->__start);
e01402b1 750 write_tc_c0_tccontext((unsigned long)0);
07cc0c9e 751
2600990e
RB
752 /*
753 * Mark the TC as activated, not interrupt exempt and not dynamically
754 * allocatable
755 */
e01402b1
RB
756 val = read_tc_c0_tcstatus();
757 val = (val & ~(TCSTATUS_DA | TCSTATUS_IXMT)) | TCSTATUS_A;
758 write_tc_c0_tcstatus(val);
759
760 write_tc_c0_tchalt(read_tc_c0_tchalt() & ~TCHALT_H);
761
e01402b1
RB
762 /*
763 * The sde-kit passes 'memsize' to __start in $a3, so set something
2600990e 764 * here... Or set $a3 to zero and define DFLT_STACK_SIZE and
e01402b1
RB
765 * DFLT_HEAP_SIZE when you compile your program
766 */
41790e04 767 mttgpr(6, v->ntcs);
07cc0c9e 768 mttgpr(7, physical_memsize);
2600990e
RB
769
770 /* set up VPE1 */
771 /*
772 * bind the TC to VPE 1 as late as possible so we only have the final
773 * VPE registers to set up, and so an EJTAG probe can trigger on it
774 */
07cc0c9e 775 write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | 1);
e01402b1 776
a94d7020
EO
777 write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~(VPECONF0_VPA));
778
779 back_to_back_c0_hazard();
780
e0daad44
RB
781 /* Set up the XTC bit in vpeconf0 to point at our tc */
782 write_vpe_c0_vpeconf0( (read_vpe_c0_vpeconf0() & ~(VPECONF0_XTC))
783 | (t->index << VPECONF0_XTC_SHIFT));
e01402b1 784
a94d7020
EO
785 back_to_back_c0_hazard();
786
e0daad44
RB
787 /* enable this VPE */
788 write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA);
e01402b1
RB
789
790 /* clear out any left overs from a previous program */
2600990e 791 write_vpe_c0_status(0);
e01402b1
RB
792 write_vpe_c0_cause(0);
793
794 /* take system out of configuration state */
340ee4b9 795 clear_c0_mvpcontrol(MVPCONTROL_VPC);
e01402b1 796
b618336a
KK
797 /*
798 * SMTC/SMVP kernels manage VPE enable independently,
799 * but uniprocessor kernels need to turn it on, even
800 * if that wasn't the pre-dvpe() state.
801 */
07cc0c9e 802#ifdef CONFIG_SMP
07cc0c9e 803 evpe(vpeflags);
b618336a
KK
804#else
805 evpe(EVPE_ENABLE);
07cc0c9e
RB
806#endif
807 emt(dmt_flag);
808 local_irq_restore(flags);
e01402b1 809
07cc0c9e
RB
810 list_for_each_entry(n, &v->notify, list)
811 n->start(minor);
2600990e 812
e01402b1
RB
813 return 0;
814}
815
2600990e 816static int find_vpe_symbols(struct vpe * v, Elf_Shdr * sechdrs,
e01402b1
RB
817 unsigned int symindex, const char *strtab,
818 struct module *mod)
819{
820 Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
821 unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
822
823 for (i = 1; i < n; i++) {
824 if (strcmp(strtab + sym[i].st_name, "__start") == 0) {
825 v->__start = sym[i].st_value;
826 }
827
828 if (strcmp(strtab + sym[i].st_name, "vpe_shared") == 0) {
829 v->shared_ptr = (void *)sym[i].st_value;
830 }
831 }
832
2600990e
RB
833 if ( (v->__start == 0) || (v->shared_ptr == NULL))
834 return -1;
835
e01402b1
RB
836 return 0;
837}
838
307bd284 839/*
2600990e
RB
840 * Allocates a VPE with some program code space(the load address), copies the
841 * contents of the program (p)buffer performing relocatations/etc, free's it
842 * when finished.
843 */
be6e1437 844static int vpe_elfload(struct vpe * v)
e01402b1
RB
845{
846 Elf_Ehdr *hdr;
847 Elf_Shdr *sechdrs;
848 long err = 0;
849 char *secstrings, *strtab = NULL;
2600990e 850 unsigned int len, i, symindex = 0, strindex = 0, relocate = 0;
e01402b1
RB
851 struct module mod; // so we can re-use the relocations code
852
853 memset(&mod, 0, sizeof(struct module));
2600990e 854 strcpy(mod.name, "VPE loader");
e01402b1
RB
855
856 hdr = (Elf_Ehdr *) v->pbuffer;
857 len = v->plen;
858
859 /* Sanity checks against insmoding binaries or wrong arch,
860 weird elf version */
d303f4a1 861 if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0
2600990e
RB
862 || (hdr->e_type != ET_REL && hdr->e_type != ET_EXEC)
863 || !elf_check_arch(hdr)
e01402b1
RB
864 || hdr->e_shentsize != sizeof(*sechdrs)) {
865 printk(KERN_WARNING
2600990e 866 "VPE loader: program wrong arch or weird elf version\n");
e01402b1
RB
867
868 return -ENOEXEC;
869 }
870
2600990e
RB
871 if (hdr->e_type == ET_REL)
872 relocate = 1;
873
e01402b1 874 if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) {
2600990e
RB
875 printk(KERN_ERR "VPE loader: program length %u truncated\n",
876 len);
877
e01402b1
RB
878 return -ENOEXEC;
879 }
880
881 /* Convenience variables */
882 sechdrs = (void *)hdr + hdr->e_shoff;
883 secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
884 sechdrs[0].sh_addr = 0;
885
886 /* And these should exist, but gcc whinges if we don't init them */
887 symindex = strindex = 0;
888
2600990e
RB
889 if (relocate) {
890 for (i = 1; i < hdr->e_shnum; i++) {
891 if (sechdrs[i].sh_type != SHT_NOBITS
892 && len < sechdrs[i].sh_offset + sechdrs[i].sh_size) {
893 printk(KERN_ERR "VPE program length %u truncated\n",
894 len);
895 return -ENOEXEC;
896 }
e01402b1 897
2600990e
RB
898 /* Mark all sections sh_addr with their address in the
899 temporary image. */
900 sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
e01402b1 901
2600990e
RB
902 /* Internal symbols and strings. */
903 if (sechdrs[i].sh_type == SHT_SYMTAB) {
904 symindex = i;
905 strindex = sechdrs[i].sh_link;
906 strtab = (char *)hdr + sechdrs[strindex].sh_offset;
907 }
e01402b1 908 }
2600990e 909 layout_sections(&mod, hdr, sechdrs, secstrings);
e01402b1
RB
910 }
911
e01402b1 912 v->load_addr = alloc_progmem(mod.core_size);
5408c490
RB
913 if (!v->load_addr)
914 return -ENOMEM;
e01402b1 915
5408c490 916 pr_info("VPE loader: loading to %p\n", v->load_addr);
e01402b1 917
2600990e
RB
918 if (relocate) {
919 for (i = 0; i < hdr->e_shnum; i++) {
920 void *dest;
e01402b1 921
2600990e
RB
922 if (!(sechdrs[i].sh_flags & SHF_ALLOC))
923 continue;
e01402b1 924
2600990e 925 dest = v->load_addr + sechdrs[i].sh_entsize;
e01402b1 926
2600990e
RB
927 if (sechdrs[i].sh_type != SHT_NOBITS)
928 memcpy(dest, (void *)sechdrs[i].sh_addr,
929 sechdrs[i].sh_size);
930 /* Update sh_addr to point to copy in image. */
931 sechdrs[i].sh_addr = (unsigned long)dest;
e01402b1 932
2600990e
RB
933 printk(KERN_DEBUG " section sh_name %s sh_addr 0x%x\n",
934 secstrings + sechdrs[i].sh_name, sechdrs[i].sh_addr);
935 }
e01402b1 936
2600990e
RB
937 /* Fix up syms, so that st_value is a pointer to location. */
938 simplify_symbols(sechdrs, symindex, strtab, secstrings,
939 hdr->e_shnum, &mod);
940
941 /* Now do relocations. */
942 for (i = 1; i < hdr->e_shnum; i++) {
943 const char *strtab = (char *)sechdrs[strindex].sh_addr;
944 unsigned int info = sechdrs[i].sh_info;
945
946 /* Not a valid relocation section? */
947 if (info >= hdr->e_shnum)
948 continue;
949
950 /* Don't bother with non-allocated sections */
951 if (!(sechdrs[info].sh_flags & SHF_ALLOC))
952 continue;
953
954 if (sechdrs[i].sh_type == SHT_REL)
955 err = apply_relocations(sechdrs, strtab, symindex, i,
956 &mod);
957 else if (sechdrs[i].sh_type == SHT_RELA)
958 err = apply_relocate_add(sechdrs, strtab, symindex, i,
959 &mod);
960 if (err < 0)
961 return err;
962
963 }
964 } else {
bdf5d42c 965 struct elf_phdr *phdr = (struct elf_phdr *) ((char *)hdr + hdr->e_phoff);
2600990e 966
bdf5d42c 967 for (i = 0; i < hdr->e_phnum; i++) {
b618336a
KK
968 if (phdr->p_type == PT_LOAD) {
969 memcpy((void *)phdr->p_paddr,
970 (char *)hdr + phdr->p_offset,
971 phdr->p_filesz);
972 memset((void *)phdr->p_paddr + phdr->p_filesz,
973 0, phdr->p_memsz - phdr->p_filesz);
974 }
975 phdr++;
bdf5d42c
RB
976 }
977
978 for (i = 0; i < hdr->e_shnum; i++) {
2600990e
RB
979 /* Internal symbols and strings. */
980 if (sechdrs[i].sh_type == SHT_SYMTAB) {
981 symindex = i;
982 strindex = sechdrs[i].sh_link;
983 strtab = (char *)hdr + sechdrs[strindex].sh_offset;
984
985 /* mark the symtab's address for when we try to find the
986 magic symbols */
987 sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
988 }
e01402b1
RB
989 }
990 }
991
992 /* make sure it's physically written out */
993 flush_icache_range((unsigned long)v->load_addr,
994 (unsigned long)v->load_addr + v->len);
995
996 if ((find_vpe_symbols(v, sechdrs, symindex, strtab, &mod)) < 0) {
2600990e
RB
997 if (v->__start == 0) {
998 printk(KERN_WARNING "VPE loader: program does not contain "
999 "a __start symbol\n");
1000 return -ENOEXEC;
1001 }
e01402b1 1002
2600990e
RB
1003 if (v->shared_ptr == NULL)
1004 printk(KERN_WARNING "VPE loader: "
1005 "program does not contain vpe_shared symbol.\n"
1006 " Unable to use AMVP (AP/SP) facilities.\n");
e01402b1
RB
1007 }
1008
1009 printk(" elf loaded\n");
2600990e 1010 return 0;
e01402b1
RB
1011}
1012
2600990e
RB
1013static void cleanup_tc(struct tc *tc)
1014{
07cc0c9e
RB
1015 unsigned long flags;
1016 unsigned int mtflags, vpflags;
2600990e
RB
1017 int tmp;
1018
07cc0c9e
RB
1019 local_irq_save(flags);
1020 mtflags = dmt();
1021 vpflags = dvpe();
2600990e
RB
1022 /* Put MVPE's into 'configuration state' */
1023 set_c0_mvpcontrol(MVPCONTROL_VPC);
1024
1025 settc(tc->index);
1026 tmp = read_tc_c0_tcstatus();
1027
1028 /* mark not allocated and not dynamically allocatable */
1029 tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1030 tmp |= TCSTATUS_IXMT; /* interrupt exempt */
1031 write_tc_c0_tcstatus(tmp);
1032
1033 write_tc_c0_tchalt(TCHALT_H);
7c3a622d 1034 mips_ihb();
2600990e
RB
1035
1036 /* bind it to anything other than VPE1 */
07cc0c9e 1037// write_tc_c0_tcbind(read_tc_c0_tcbind() & ~TCBIND_CURVPE); // | TCBIND_CURVPE
2600990e
RB
1038
1039 clear_c0_mvpcontrol(MVPCONTROL_VPC);
07cc0c9e
RB
1040 evpe(vpflags);
1041 emt(mtflags);
1042 local_irq_restore(flags);
2600990e
RB
1043}
1044
1045static int getcwd(char *buff, int size)
1046{
1047 mm_segment_t old_fs;
1048 int ret;
1049
1050 old_fs = get_fs();
1051 set_fs(KERNEL_DS);
1052
21a151d8 1053 ret = sys_getcwd(buff, size);
2600990e
RB
1054
1055 set_fs(old_fs);
1056
1057 return ret;
1058}
1059
1060/* checks VPE is unused and gets ready to load program */
e01402b1
RB
1061static int vpe_open(struct inode *inode, struct file *filp)
1062{
c4c4018b 1063 enum vpe_state state;
2600990e 1064 struct vpe_notifications *not;
07cc0c9e 1065 struct vpe *v;
1bbfc20d 1066 int ret;
e01402b1 1067
07cc0c9e
RB
1068 if (minor != iminor(inode)) {
1069 /* assume only 1 device at the moment. */
1bbfc20d
RB
1070 pr_warning("VPE loader: only vpe1 is supported\n");
1071
1072 return -ENODEV;
e01402b1
RB
1073 }
1074
07cc0c9e 1075 if ((v = get_vpe(tclimit)) == NULL) {
1bbfc20d
RB
1076 pr_warning("VPE loader: unable to get vpe\n");
1077
1078 return -ENODEV;
e01402b1
RB
1079 }
1080
c4c4018b
RB
1081 state = xchg(&v->state, VPE_STATE_INUSE);
1082 if (state != VPE_STATE_UNUSED) {
2600990e 1083 printk(KERN_DEBUG "VPE loader: tc in use dumping regs\n");
e01402b1 1084
2600990e 1085 list_for_each_entry(not, &v->notify, list) {
07cc0c9e 1086 not->stop(tclimit);
2600990e 1087 }
e01402b1 1088
2600990e 1089 release_progmem(v->load_addr);
07cc0c9e 1090 cleanup_tc(get_tc(tclimit));
e01402b1
RB
1091 }
1092
e01402b1
RB
1093 /* this of-course trashes what was there before... */
1094 v->pbuffer = vmalloc(P_SIZE);
863abad4
JJ
1095 if (!v->pbuffer) {
1096 pr_warning("VPE loader: unable to allocate memory\n");
1097 return -ENOMEM;
1098 }
e01402b1
RB
1099 v->plen = P_SIZE;
1100 v->load_addr = NULL;
1101 v->len = 0;
1102
d76b0d9b
DH
1103 v->uid = filp->f_cred->fsuid;
1104 v->gid = filp->f_cred->fsgid;
2600990e
RB
1105
1106#ifdef CONFIG_MIPS_APSP_KSPD
1107 /* get kspd to tell us when a syscall_exit happens */
1108 if (!kspd_events_reqd) {
1109 kspd_notify(&kspd_events);
1110 kspd_events_reqd++;
1111 }
1112#endif
1113
1114 v->cwd[0] = 0;
1115 ret = getcwd(v->cwd, VPE_PATH_MAX);
1116 if (ret < 0)
1117 printk(KERN_WARNING "VPE loader: open, getcwd returned %d\n", ret);
1118
1119 v->shared_ptr = NULL;
1120 v->__start = 0;
07cc0c9e 1121
e01402b1
RB
1122 return 0;
1123}
1124
1125static int vpe_release(struct inode *inode, struct file *filp)
1126{
307bd284 1127 struct vpe *v;
e01402b1 1128 Elf_Ehdr *hdr;
07cc0c9e 1129 int ret = 0;
e01402b1 1130
07cc0c9e
RB
1131 v = get_vpe(tclimit);
1132 if (v == NULL)
e01402b1
RB
1133 return -ENODEV;
1134
e01402b1 1135 hdr = (Elf_Ehdr *) v->pbuffer;
d303f4a1 1136 if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) == 0) {
07cc0c9e 1137 if (vpe_elfload(v) >= 0) {
e01402b1 1138 vpe_run(v);
07cc0c9e 1139 } else {
2600990e 1140 printk(KERN_WARNING "VPE loader: ELF load failed.\n");
e01402b1
RB
1141 ret = -ENOEXEC;
1142 }
1143 } else {
2600990e 1144 printk(KERN_WARNING "VPE loader: only elf files are supported\n");
e01402b1
RB
1145 ret = -ENOEXEC;
1146 }
1147
2600990e
RB
1148 /* It's good to be able to run the SP and if it chokes have a look at
1149 the /dev/rt?. But if we reset the pointer to the shared struct we
8ebcfc8b 1150 lose what has happened. So perhaps if garbage is sent to the vpe
2600990e
RB
1151 device, use it as a trigger for the reset. Hopefully a nice
1152 executable will be along shortly. */
1153 if (ret < 0)
1154 v->shared_ptr = NULL;
1155
863abad4 1156 vfree(v->pbuffer);
e01402b1 1157 v->plen = 0;
863abad4 1158
e01402b1
RB
1159 return ret;
1160}
1161
1162static ssize_t vpe_write(struct file *file, const char __user * buffer,
1163 size_t count, loff_t * ppos)
1164{
e01402b1 1165 size_t ret = count;
307bd284 1166 struct vpe *v;
e01402b1 1167
07cc0c9e
RB
1168 if (iminor(file->f_path.dentry->d_inode) != minor)
1169 return -ENODEV;
1170
1171 v = get_vpe(tclimit);
1172 if (v == NULL)
e01402b1
RB
1173 return -ENODEV;
1174
e01402b1
RB
1175 if ((count + v->len) > v->plen) {
1176 printk(KERN_WARNING
2600990e 1177 "VPE loader: elf size too big. Perhaps strip uneeded symbols\n");
e01402b1
RB
1178 return -ENOMEM;
1179 }
1180
1181 count -= copy_from_user(v->pbuffer + v->len, buffer, count);
2600990e 1182 if (!count)
e01402b1 1183 return -EFAULT;
e01402b1
RB
1184
1185 v->len += count;
1186 return ret;
1187}
1188
5dfe4c96 1189static const struct file_operations vpe_fops = {
e01402b1
RB
1190 .owner = THIS_MODULE,
1191 .open = vpe_open,
1192 .release = vpe_release,
6038f373
AB
1193 .write = vpe_write,
1194 .llseek = noop_llseek,
e01402b1
RB
1195};
1196
1197/* module wrapper entry points */
1198/* give me a vpe */
1199vpe_handle vpe_alloc(void)
1200{
1201 int i;
1202 struct vpe *v;
1203
1204 /* find a vpe */
1205 for (i = 1; i < MAX_VPES; i++) {
1206 if ((v = get_vpe(i)) != NULL) {
1207 v->state = VPE_STATE_INUSE;
1208 return v;
1209 }
1210 }
1211 return NULL;
1212}
1213
1214EXPORT_SYMBOL(vpe_alloc);
1215
1216/* start running from here */
1217int vpe_start(vpe_handle vpe, unsigned long start)
1218{
1219 struct vpe *v = vpe;
1220
1221 v->__start = start;
1222 return vpe_run(v);
1223}
1224
1225EXPORT_SYMBOL(vpe_start);
1226
1227/* halt it for now */
1228int vpe_stop(vpe_handle vpe)
1229{
1230 struct vpe *v = vpe;
1231 struct tc *t;
1232 unsigned int evpe_flags;
1233
1234 evpe_flags = dvpe();
1235
1236 if ((t = list_entry(v->tc.next, struct tc, tc)) != NULL) {
1237
1238 settc(t->index);
1239 write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
1240 }
1241
1242 evpe(evpe_flags);
1243
1244 return 0;
1245}
1246
1247EXPORT_SYMBOL(vpe_stop);
1248
1249/* I've done with it thank you */
1250int vpe_free(vpe_handle vpe)
1251{
1252 struct vpe *v = vpe;
1253 struct tc *t;
1254 unsigned int evpe_flags;
1255
1256 if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
1257 return -ENOEXEC;
1258 }
1259
1260 evpe_flags = dvpe();
1261
1262 /* Put MVPE's into 'configuration state' */
340ee4b9 1263 set_c0_mvpcontrol(MVPCONTROL_VPC);
e01402b1
RB
1264
1265 settc(t->index);
1266 write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
1267
7c3a622d 1268 /* halt the TC */
e01402b1 1269 write_tc_c0_tchalt(TCHALT_H);
7c3a622d
NS
1270 mips_ihb();
1271
1272 /* mark the TC unallocated */
1273 write_tc_c0_tcstatus(read_tc_c0_tcstatus() & ~TCSTATUS_A);
e01402b1
RB
1274
1275 v->state = VPE_STATE_UNUSED;
1276
340ee4b9 1277 clear_c0_mvpcontrol(MVPCONTROL_VPC);
e01402b1
RB
1278 evpe(evpe_flags);
1279
1280 return 0;
1281}
1282
1283EXPORT_SYMBOL(vpe_free);
1284
1285void *vpe_get_shared(int index)
1286{
1287 struct vpe *v;
1288
2600990e 1289 if ((v = get_vpe(index)) == NULL)
e01402b1 1290 return NULL;
e01402b1
RB
1291
1292 return v->shared_ptr;
1293}
1294
1295EXPORT_SYMBOL(vpe_get_shared);
1296
2600990e
RB
1297int vpe_getuid(int index)
1298{
1299 struct vpe *v;
1300
1301 if ((v = get_vpe(index)) == NULL)
1302 return -1;
1303
1304 return v->uid;
1305}
1306
1307EXPORT_SYMBOL(vpe_getuid);
1308
1309int vpe_getgid(int index)
1310{
1311 struct vpe *v;
1312
1313 if ((v = get_vpe(index)) == NULL)
1314 return -1;
1315
1316 return v->gid;
1317}
1318
1319EXPORT_SYMBOL(vpe_getgid);
1320
1321int vpe_notify(int index, struct vpe_notifications *notify)
1322{
1323 struct vpe *v;
1324
1325 if ((v = get_vpe(index)) == NULL)
1326 return -1;
1327
1328 list_add(&notify->list, &v->notify);
1329 return 0;
1330}
1331
1332EXPORT_SYMBOL(vpe_notify);
1333
1334char *vpe_getcwd(int index)
1335{
1336 struct vpe *v;
1337
1338 if ((v = get_vpe(index)) == NULL)
1339 return NULL;
1340
1341 return v->cwd;
1342}
1343
1344EXPORT_SYMBOL(vpe_getcwd);
1345
1346#ifdef CONFIG_MIPS_APSP_KSPD
1347static void kspd_sp_exit( int sp_id)
1348{
1349 cleanup_tc(get_tc(sp_id));
1350}
1351#endif
1352
736fad17
KS
1353static ssize_t store_kill(struct device *dev, struct device_attribute *attr,
1354 const char *buf, size_t len)
0f5d0df3
RB
1355{
1356 struct vpe *vpe = get_vpe(tclimit);
1357 struct vpe_notifications *not;
1358
1359 list_for_each_entry(not, &vpe->notify, list) {
1360 not->stop(tclimit);
1361 }
1362
1363 release_progmem(vpe->load_addr);
1364 cleanup_tc(get_tc(tclimit));
1365 vpe_stop(vpe);
1366 vpe_free(vpe);
1367
1368 return len;
1369}
1370
736fad17
KS
1371static ssize_t show_ntcs(struct device *cd, struct device_attribute *attr,
1372 char *buf)
41790e04
RB
1373{
1374 struct vpe *vpe = get_vpe(tclimit);
1375
1376 return sprintf(buf, "%d\n", vpe->ntcs);
1377}
1378
736fad17
KS
1379static ssize_t store_ntcs(struct device *dev, struct device_attribute *attr,
1380 const char *buf, size_t len)
41790e04
RB
1381{
1382 struct vpe *vpe = get_vpe(tclimit);
1383 unsigned long new;
1384 char *endp;
1385
1386 new = simple_strtoul(buf, &endp, 0);
1387 if (endp == buf)
1388 goto out_einval;
1389
1390 if (new == 0 || new > (hw_tcs - tclimit))
1391 goto out_einval;
1392
1393 vpe->ntcs = new;
1394
1395 return len;
1396
1397out_einval:
52a7a27c 1398 return -EINVAL;
41790e04
RB
1399}
1400
736fad17 1401static struct device_attribute vpe_class_attributes[] = {
0f5d0df3 1402 __ATTR(kill, S_IWUSR, NULL, store_kill),
41790e04
RB
1403 __ATTR(ntcs, S_IRUGO | S_IWUSR, show_ntcs, store_ntcs),
1404 {}
1405};
1406
736fad17 1407static void vpe_device_release(struct device *cd)
41790e04
RB
1408{
1409 kfree(cd);
1410}
1411
1412struct class vpe_class = {
1413 .name = "vpe",
1414 .owner = THIS_MODULE,
736fad17
KS
1415 .dev_release = vpe_device_release,
1416 .dev_attrs = vpe_class_attributes,
41790e04
RB
1417};
1418
736fad17 1419struct device vpe_device;
27a3bbaf 1420
e01402b1
RB
1421static int __init vpe_module_init(void)
1422{
07cc0c9e 1423 unsigned int mtflags, vpflags;
07cc0c9e 1424 unsigned long flags, val;
e01402b1
RB
1425 struct vpe *v = NULL;
1426 struct tc *t;
41790e04 1427 int tc, err;
e01402b1
RB
1428
1429 if (!cpu_has_mipsmt) {
1430 printk("VPE loader: not a MIPS MT capable processor\n");
1431 return -ENODEV;
1432 }
1433
07cc0c9e
RB
1434 if (vpelimit == 0) {
1435 printk(KERN_WARNING "No VPEs reserved for AP/SP, not "
1436 "initializing VPE loader.\nPass maxvpes=<n> argument as "
1437 "kernel argument\n");
1438
1439 return -ENODEV;
1440 }
1441
1442 if (tclimit == 0) {
1443 printk(KERN_WARNING "No TCs reserved for AP/SP, not "
1444 "initializing VPE loader.\nPass maxtcs=<n> argument as "
1445 "kernel argument\n");
1446
1447 return -ENODEV;
1448 }
1449
682e852e
AD
1450 major = register_chrdev(0, module_name, &vpe_fops);
1451 if (major < 0) {
e01402b1 1452 printk("VPE loader: unable to register character device\n");
307bd284 1453 return major;
e01402b1
RB
1454 }
1455
41790e04
RB
1456 err = class_register(&vpe_class);
1457 if (err) {
1458 printk(KERN_ERR "vpe_class registration failed\n");
27a3bbaf
RB
1459 goto out_chrdev;
1460 }
41790e04 1461
736fad17 1462 device_initialize(&vpe_device);
41790e04
RB
1463 vpe_device.class = &vpe_class,
1464 vpe_device.parent = NULL,
1bb5beb4 1465 dev_set_name(&vpe_device, "vpe1");
41790e04 1466 vpe_device.devt = MKDEV(major, minor);
736fad17 1467 err = device_add(&vpe_device);
41790e04
RB
1468 if (err) {
1469 printk(KERN_ERR "Adding vpe_device failed\n");
1470 goto out_class;
1471 }
27a3bbaf 1472
07cc0c9e
RB
1473 local_irq_save(flags);
1474 mtflags = dmt();
1475 vpflags = dvpe();
e01402b1
RB
1476
1477 /* Put MVPE's into 'configuration state' */
340ee4b9 1478 set_c0_mvpcontrol(MVPCONTROL_VPC);
e01402b1
RB
1479
1480 /* dump_mtregs(); */
1481
e01402b1 1482 val = read_c0_mvpconf0();
07cc0c9e
RB
1483 hw_tcs = (val & MVPCONF0_PTC) + 1;
1484 hw_vpes = ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1;
1485
1486 for (tc = tclimit; tc < hw_tcs; tc++) {
1487 /*
1488 * Must re-enable multithreading temporarily or in case we
1489 * reschedule send IPIs or similar we might hang.
1490 */
1491 clear_c0_mvpcontrol(MVPCONTROL_VPC);
1492 evpe(vpflags);
1493 emt(mtflags);
1494 local_irq_restore(flags);
1495 t = alloc_tc(tc);
1496 if (!t) {
1497 err = -ENOMEM;
1498 goto out;
1499 }
1500
1501 local_irq_save(flags);
1502 mtflags = dmt();
1503 vpflags = dvpe();
1504 set_c0_mvpcontrol(MVPCONTROL_VPC);
e01402b1
RB
1505
1506 /* VPE's */
07cc0c9e
RB
1507 if (tc < hw_tcs) {
1508 settc(tc);
e01402b1 1509
07cc0c9e 1510 if ((v = alloc_vpe(tc)) == NULL) {
e01402b1 1511 printk(KERN_WARNING "VPE: unable to allocate VPE\n");
07cc0c9e
RB
1512
1513 goto out_reenable;
e01402b1
RB
1514 }
1515
41790e04
RB
1516 v->ntcs = hw_tcs - tclimit;
1517
2600990e
RB
1518 /* add the tc to the list of this vpe's tc's. */
1519 list_add(&t->tc, &v->tc);
e01402b1
RB
1520
1521 /* deactivate all but vpe0 */
07cc0c9e 1522 if (tc >= tclimit) {
e01402b1
RB
1523 unsigned long tmp = read_vpe_c0_vpeconf0();
1524
1525 tmp &= ~VPECONF0_VPA;
1526
1527 /* master VPE */
1528 tmp |= VPECONF0_MVP;
1529 write_vpe_c0_vpeconf0(tmp);
1530 }
1531
1532 /* disable multi-threading with TC's */
1533 write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE);
1534
07cc0c9e 1535 if (tc >= vpelimit) {
2600990e
RB
1536 /*
1537 * Set config to be the same as vpe0,
1538 * particularly kseg0 coherency alg
1539 */
e01402b1
RB
1540 write_vpe_c0_config(read_c0_config());
1541 }
e01402b1
RB
1542 }
1543
1544 /* TC's */
1545 t->pvpe = v; /* set the parent vpe */
1546
07cc0c9e 1547 if (tc >= tclimit) {
e01402b1
RB
1548 unsigned long tmp;
1549
07cc0c9e 1550 settc(tc);
e01402b1 1551
2600990e
RB
1552 /* Any TC that is bound to VPE0 gets left as is - in case
1553 we are running SMTC on VPE0. A TC that is bound to any
1554 other VPE gets bound to VPE0, ideally I'd like to make
1555 it homeless but it doesn't appear to let me bind a TC
1556 to a non-existent VPE. Which is perfectly reasonable.
1557
1558 The (un)bound state is visible to an EJTAG probe so may
1559 notify GDB...
1560 */
1561
1562 if (((tmp = read_tc_c0_tcbind()) & TCBIND_CURVPE)) {
1563 /* tc is bound >vpe0 */
1564 write_tc_c0_tcbind(tmp & ~TCBIND_CURVPE);
1565
1566 t->pvpe = get_vpe(0); /* set the parent vpe */
1567 }
e01402b1 1568
7c3a622d
NS
1569 /* halt the TC */
1570 write_tc_c0_tchalt(TCHALT_H);
1571 mips_ihb();
1572
e01402b1
RB
1573 tmp = read_tc_c0_tcstatus();
1574
2600990e 1575 /* mark not activated and not dynamically allocatable */
e01402b1
RB
1576 tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1577 tmp |= TCSTATUS_IXMT; /* interrupt exempt */
1578 write_tc_c0_tcstatus(tmp);
e01402b1
RB
1579 }
1580 }
1581
07cc0c9e 1582out_reenable:
e01402b1 1583 /* release config state */
340ee4b9 1584 clear_c0_mvpcontrol(MVPCONTROL_VPC);
e01402b1 1585
07cc0c9e
RB
1586 evpe(vpflags);
1587 emt(mtflags);
1588 local_irq_restore(flags);
1589
2600990e
RB
1590#ifdef CONFIG_MIPS_APSP_KSPD
1591 kspd_events.kspd_sp_exit = kspd_sp_exit;
1592#endif
e01402b1 1593 return 0;
27a3bbaf 1594
41790e04
RB
1595out_class:
1596 class_unregister(&vpe_class);
27a3bbaf
RB
1597out_chrdev:
1598 unregister_chrdev(major, module_name);
1599
07cc0c9e 1600out:
27a3bbaf 1601 return err;
e01402b1
RB
1602}
1603
1604static void __exit vpe_module_exit(void)
1605{
1606 struct vpe *v, *n;
1607
1bbfc20d
RB
1608 device_del(&vpe_device);
1609 unregister_chrdev(major, module_name);
1610
1611 /* No locking needed here */
e01402b1 1612 list_for_each_entry_safe(v, n, &vpecontrol.vpe_list, list) {
1bbfc20d 1613 if (v->state != VPE_STATE_UNUSED)
e01402b1 1614 release_vpe(v);
e01402b1 1615 }
e01402b1
RB
1616}
1617
1618module_init(vpe_module_init);
1619module_exit(vpe_module_exit);
1620MODULE_DESCRIPTION("MIPS VPE Loader");
2600990e 1621MODULE_AUTHOR("Elizabeth Oldham, MIPS Technologies, Inc.");
e01402b1 1622MODULE_LICENSE("GPL");