~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/mips/kernel/vpe.c

Version: ~ [ linux-5.9 ] ~ [ linux-5.8.14 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.70 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.150 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.200 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.238 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.238 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.85 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp