📄 vpe.c
字号:
{ Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr; unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym); printk(KERN_DEBUG "dump_elfsymbols: n %d\n", n); for (i = 1; i < n; i++) { printk(KERN_DEBUG " i %d name <%s> 0x%x\n", i, strtab + sym[i].st_name, sym[i].st_value); }}#endifstatic void dump_tc(struct tc *t){ printk(KERN_WARNING "VPE: TC index %d TCStatus 0x%lx halt 0x%lx\n", t->index, read_tc_c0_tcstatus(), read_tc_c0_tchalt()); printk(KERN_WARNING "VPE: tcrestart 0x%lx\n", read_tc_c0_tcrestart());}static void dump_tclist(void){ struct tc *t; list_for_each_entry(t, &vpecontrol.tc_list, list) { dump_tc(t); }}/* We are prepared so configure and start the VPE... */int vpe_run(struct vpe * v){ unsigned long val; struct tc *t; /* check we are the Master VPE */ val = read_c0_vpeconf0(); if (!(val & VPECONF0_MVP)) { printk(KERN_WARNING "VPE: only Master VPE's are allowed to configure MT\n"); return -1; } /* disable MT (using dvpe) */ dvpe(); /* Put MVPE's into 'configuration state' */ set_c0_mvpcontrol(MVPCONTROL_VPC); if (!list_empty(&v->tc)) { if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) { printk(KERN_WARNING "VPE: TC %d is already in use.\n", t->index); return -ENOEXEC; } } else { printk(KERN_WARNING "VPE: No TC's associated with VPE %d\n", v->minor); return -ENOEXEC; } settc(t->index); val = read_vpe_c0_vpeconf0(); /* should check it is halted, and not activated */ if ((read_tc_c0_tcstatus() & TCSTATUS_A) || !(read_tc_c0_tchalt() & TCHALT_H)) { printk(KERN_WARNING "VPE: TC %d is already doing something!\n", t->index); dump_tclist(); return -ENOEXEC; } /* Write the address we want it to start running from in the TCPC register. */ write_tc_c0_tcrestart((unsigned long)v->__start); /* write the sivc_info address to tccontext */ write_tc_c0_tccontext((unsigned long)0); /* Set up the XTC bit in vpeconf0 to point at our tc */ write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | (t->index << VPECONF0_XTC_SHIFT)); /* mark the TC as activated, not interrupt exempt and not dynamically allocatable */ val = read_tc_c0_tcstatus(); val = (val & ~(TCSTATUS_DA | TCSTATUS_IXMT)) | TCSTATUS_A; write_tc_c0_tcstatus(val); write_tc_c0_tchalt(read_tc_c0_tchalt() & ~TCHALT_H); /* set up VPE1 */ write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE); // no multiple TC's write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA); // enable this VPE /* * The sde-kit passes 'memsize' to __start in $a3, so set something * here... * Or set $a3 (register 7) to zero and define DFLT_STACK_SIZE and * DFLT_HEAP_SIZE when you compile your program */ mttgpr(7, 0); /* set config to be the same as vpe0, particularly kseg0 coherency alg */ write_vpe_c0_config(read_c0_config()); /* clear out any left overs from a previous program */ write_vpe_c0_cause(0); /* take system out of configuration state */ clear_c0_mvpcontrol(MVPCONTROL_VPC); /* clear interrupts enabled IE, ERL, EXL, and KSU from c0 status */ write_vpe_c0_status(read_vpe_c0_status() & ~(ST0_ERL | ST0_KSU | ST0_IE | ST0_EXL)); /* set it running */ evpe(EVPE_ENABLE); return 0;}static unsigned long find_vpe_symbols(struct vpe * v, Elf_Shdr * sechdrs, unsigned int symindex, const char *strtab, struct module *mod){ Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr; unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym); for (i = 1; i < n; i++) { if (strcmp(strtab + sym[i].st_name, "__start") == 0) { v->__start = sym[i].st_value; } if (strcmp(strtab + sym[i].st_name, "vpe_shared") == 0) { v->shared_ptr = (void *)sym[i].st_value; } } return 0;}/* * Allocates a VPE with some program code space(the load address), copies * the contents of the program (p)buffer performing relocatations/etc, * free's it when finished.*/int vpe_elfload(struct vpe * v){ Elf_Ehdr *hdr; Elf_Shdr *sechdrs; long err = 0; char *secstrings, *strtab = NULL; unsigned int len, i, symindex = 0, strindex = 0; struct module mod; // so we can re-use the relocations code memset(&mod, 0, sizeof(struct module)); strcpy(mod.name, "VPE dummy prog module"); hdr = (Elf_Ehdr *) v->pbuffer; len = v->plen; /* Sanity checks against insmoding binaries or wrong arch, weird elf version */ if (memcmp(hdr->e_ident, ELFMAG, 4) != 0 || hdr->e_type != ET_REL || !elf_check_arch(hdr) || hdr->e_shentsize != sizeof(*sechdrs)) { printk(KERN_WARNING "VPE program, wrong arch or weird elf version\n"); return -ENOEXEC; } if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) { printk(KERN_ERR "VPE program length %u truncated\n", len); return -ENOEXEC; } /* Convenience variables */ sechdrs = (void *)hdr + hdr->e_shoff; secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; sechdrs[0].sh_addr = 0; /* And these should exist, but gcc whinges if we don't init them */ symindex = strindex = 0; for (i = 1; i < hdr->e_shnum; i++) { if (sechdrs[i].sh_type != SHT_NOBITS && len < sechdrs[i].sh_offset + sechdrs[i].sh_size) { printk(KERN_ERR "VPE program length %u truncated\n", len); return -ENOEXEC; } /* Mark all sections sh_addr with their address in the temporary image. */ sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset; /* Internal symbols and strings. */ if (sechdrs[i].sh_type == SHT_SYMTAB) { symindex = i; strindex = sechdrs[i].sh_link; strtab = (char *)hdr + sechdrs[strindex].sh_offset; } } layout_sections(&mod, hdr, sechdrs, secstrings); v->load_addr = alloc_progmem(mod.core_size); memset(v->load_addr, 0, mod.core_size); printk("VPE elf_loader: loading to %p\n", v->load_addr); for (i = 0; i < hdr->e_shnum; i++) { void *dest; if (!(sechdrs[i].sh_flags & SHF_ALLOC)) continue; dest = v->load_addr + sechdrs[i].sh_entsize; if (sechdrs[i].sh_type != SHT_NOBITS) memcpy(dest, (void *)sechdrs[i].sh_addr, sechdrs[i].sh_size); /* Update sh_addr to point to copy in image. */ sechdrs[i].sh_addr = (unsigned long)dest; } /* Fix up syms, so that st_value is a pointer to location. */ err = simplify_symbols(sechdrs, symindex, strtab, secstrings, hdr->e_shnum, &mod); if (err < 0) { printk(KERN_WARNING "VPE: unable to simplify symbols\n"); goto cleanup; } /* Now do relocations. */ for (i = 1; i < hdr->e_shnum; i++) { const char *strtab = (char *)sechdrs[strindex].sh_addr; unsigned int info = sechdrs[i].sh_info; /* Not a valid relocation section? */ if (info >= hdr->e_shnum) continue; /* Don't bother with non-allocated sections */ if (!(sechdrs[info].sh_flags & SHF_ALLOC)) continue; if (sechdrs[i].sh_type == SHT_REL) err = apply_relocations(sechdrs, strtab, symindex, i, &mod); else if (sechdrs[i].sh_type == SHT_RELA) err = apply_relocate_add(sechdrs, strtab, symindex, i, &mod); if (err < 0) { printk(KERN_WARNING "vpe_elfload: error in relocations err %ld\n", err); goto cleanup; } } /* make sure it's physically written out */ flush_icache_range((unsigned long)v->load_addr, (unsigned long)v->load_addr + v->len); if ((find_vpe_symbols(v, sechdrs, symindex, strtab, &mod)) < 0) { printk(KERN_WARNING "VPE: program doesn't contain __start or vpe_shared symbols\n"); err = -ENOEXEC; } printk(" elf loaded\n");cleanup: return err;}static void dump_vpe(struct vpe * v){ struct tc *t; printk(KERN_DEBUG "VPEControl 0x%lx\n", read_vpe_c0_vpecontrol()); printk(KERN_DEBUG "VPEConf0 0x%lx\n", read_vpe_c0_vpeconf0()); list_for_each_entry(t, &vpecontrol.tc_list, list) { dump_tc(t); }}/* checks for VPE is unused and gets ready to load program */static int vpe_open(struct inode *inode, struct file *filp){ int minor; struct vpe *v; /* assume only 1 device at the mo. */ if ((minor = MINOR(inode->i_rdev)) != 1) { printk(KERN_WARNING "VPE: only vpe1 is supported\n"); return -ENODEV; } if ((v = get_vpe(minor)) == NULL) { printk(KERN_WARNING "VPE: unable to get vpe\n"); return -ENODEV; } if (v->state != VPE_STATE_UNUSED) { unsigned long tmp; struct tc *t; printk(KERN_WARNING "VPE: device %d already in use\n", minor); dvpe(); dump_vpe(v); printk(KERN_WARNING "VPE: re-initialising %d\n", minor); release_progmem(v->load_addr); t = get_tc(minor); settc(minor); tmp = read_tc_c0_tcstatus(); /* mark not allocated and not dynamically allocatable */ tmp &= ~(TCSTATUS_A | TCSTATUS_DA); tmp |= TCSTATUS_IXMT; /* interrupt exempt */ write_tc_c0_tcstatus(tmp); write_tc_c0_tchalt(TCHALT_H); } // allocate it so when we get write ops we know it's expected. v->state = VPE_STATE_INUSE; /* this of-course trashes what was there before... */ v->pbuffer = vmalloc(P_SIZE); v->plen = P_SIZE; v->load_addr = NULL; v->len = 0; return 0;}static int vpe_release(struct inode *inode, struct file *filp){ int minor, ret = 0; struct vpe *v; Elf_Ehdr *hdr; minor = MINOR(inode->i_rdev); if ((v = get_vpe(minor)) == NULL) return -ENODEV; // simple case of fire and forget, so tell the VPE to run... hdr = (Elf_Ehdr *) v->pbuffer; if (memcmp(hdr->e_ident, ELFMAG, 4) == 0) { if (vpe_elfload(v) >= 0) vpe_run(v); else { printk(KERN_WARNING "VPE: ELF load failed.\n"); ret = -ENOEXEC; } } else { printk(KERN_WARNING "VPE: only elf files are supported\n"); ret = -ENOEXEC; } // cleanup any temp buffers if (v->pbuffer) vfree(v->pbuffer); v->plen = 0; return ret;}static ssize_t vpe_write(struct file *file, const char __user * buffer, size_t count, loff_t * ppos){ int minor; size_t ret = count; struct vpe *v; minor = MINOR(file->f_dentry->d_inode->i_rdev); if ((v = get_vpe(minor)) == NULL) return -ENODEV; if (v->pbuffer == NULL) { printk(KERN_ERR "vpe_write: no pbuffer\n"); return -ENOMEM; } if ((count + v->len) > v->plen) { printk(KERN_WARNING "VPE Loader: elf size too big. Perhaps strip uneeded symbols\n"); return -ENOMEM; } count -= copy_from_user(v->pbuffer + v->len, buffer, count); if (!count) { printk("vpe_write: copy_to_user failed\n"); return -EFAULT; } v->len += count; return ret;}static struct file_operations vpe_fops = { .owner = THIS_MODULE, .open = vpe_open, .release = vpe_release, .write = vpe_write};/* module wrapper entry points *//* give me a vpe */vpe_handle vpe_alloc(void){ int i; struct vpe *v; /* find a vpe */ for (i = 1; i < MAX_VPES; i++) { if ((v = get_vpe(i)) != NULL) { v->state = VPE_STATE_INUSE; return v; } } return NULL;}EXPORT_SYMBOL(vpe_alloc);/* start running from here */int vpe_start(vpe_handle vpe, unsigned long start){ struct vpe *v = vpe; v->__start = start; return vpe_run(v);}EXPORT_SYMBOL(vpe_start);/* halt it for now */int vpe_stop(vpe_handle vpe){ struct vpe *v = vpe; struct tc *t; unsigned int evpe_flags; evpe_flags = dvpe(); if ((t = list_entry(v->tc.next, struct tc, tc)) != NULL) { settc(t->index); write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA); } evpe(evpe_flags); return 0;}EXPORT_SYMBOL(vpe_stop);/* I've done with it thank you */int vpe_free(vpe_handle vpe){ struct vpe *v = vpe; struct tc *t; unsigned int evpe_flags; if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) { return -ENOEXEC; } evpe_flags = dvpe(); /* Put MVPE's into 'configuration state' */ set_c0_mvpcontrol(MVPCONTROL_VPC); settc(t->index); write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA); /* mark the TC unallocated and halt'ed */ write_tc_c0_tcstatus(read_tc_c0_tcstatus() & ~TCSTATUS_A); write_tc_c0_tchalt(TCHALT_H); v->state = VPE_STATE_UNUSED; clear_c0_mvpcontrol(MVPCONTROL_VPC); evpe(evpe_flags); return 0;}EXPORT_SYMBOL(vpe_free);void *vpe_get_shared(int index){ struct vpe *v; if ((v = get_vpe(index)) == NULL) { printk(KERN_WARNING "vpe: invalid vpe index %d\n", index); return NULL; } return v->shared_ptr;}EXPORT_SYMBOL(vpe_get_shared);static int __init vpe_module_init(void){ struct vpe *v = NULL; struct tc *t; unsigned long val; int i; if (!cpu_has_mipsmt) { printk("VPE loader: not a MIPS MT capable processor\n"); return -ENODEV; } if ((major = register_chrdev(0, module_name, &vpe_fops) < 0)) { printk("VPE loader: unable to register character device\n"); return major; } dmt(); dvpe(); /* Put MVPE's into 'configuration state' */ set_c0_mvpcontrol(MVPCONTROL_VPC); /* dump_mtregs(); */ INIT_LIST_HEAD(&vpecontrol.vpe_list); INIT_LIST_HEAD(&vpecontrol.tc_list); val = read_c0_mvpconf0(); for (i = 0; i < ((val & MVPCONF0_PTC) + 1); i++) { t = alloc_tc(i); /* VPE's */ if (i < ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1) { settc(i); if ((v = alloc_vpe(i)) == NULL) { printk(KERN_WARNING "VPE: unable to allocate VPE\n"); return -ENODEV; } list_add(&t->tc, &v->tc); /* add the tc to the list of this vpe's tc's. */ /* deactivate all but vpe0 */ if (i != 0) { unsigned long tmp = read_vpe_c0_vpeconf0(); tmp &= ~VPECONF0_VPA; /* master VPE */ tmp |= VPECONF0_MVP; write_vpe_c0_vpeconf0(tmp); } /* disable multi-threading with TC's */ write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE); if (i != 0) { write_vpe_c0_status((read_c0_status() & ~(ST0_IM | ST0_IE | ST0_KSU)) | ST0_CU0); /* set config to be the same as vpe0, particularly kseg0 coherency alg */ write_vpe_c0_config(read_c0_config()); } } /* TC's */ t->pvpe = v; /* set the parent vpe */ if (i != 0) { unsigned long tmp; /* tc 0 will of course be running.... */ if (i == 0) t->state = TC_STATE_RUNNING; settc(i); /* bind a TC to each VPE, May as well put all excess TC's on the last VPE */ if (i >= (((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1)) write_tc_c0_tcbind(read_tc_c0_tcbind() | ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT)); else write_tc_c0_tcbind(read_tc_c0_tcbind() | i); tmp = read_tc_c0_tcstatus(); /* mark not allocated and not dynamically allocatable */ tmp &= ~(TCSTATUS_A | TCSTATUS_DA); tmp |= TCSTATUS_IXMT; /* interrupt exempt */ write_tc_c0_tcstatus(tmp); write_tc_c0_tchalt(TCHALT_H); } } /* release config state */ clear_c0_mvpcontrol(MVPCONTROL_VPC); return 0;}static void __exit vpe_module_exit(void){ struct vpe *v, *n; list_for_each_entry_safe(v, n, &vpecontrol.vpe_list, list) { if (v->state != VPE_STATE_UNUSED) { release_vpe(v); } } unregister_chrdev(major, module_name);}module_init(vpe_module_init);module_exit(vpe_module_exit);MODULE_DESCRIPTION("MIPS VPE Loader");MODULE_AUTHOR("Elizabeth Clarke, MIPS Technologies, Inc");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -