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