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