📄 vpe.c
字号:
if ((find_vpe_symbols(v, sechdrs, symindex, strtab, &mod)) < 0) { if (v->__start == 0) { printk(KERN_WARNING "VPE loader: program does not contain " "a __start symbol\n"); return -ENOEXEC; } if (v->shared_ptr == NULL) printk(KERN_WARNING "VPE loader: " "program does not contain vpe_shared symbol.\n" " Unable to use AMVP (AP/SP) facilities.\n"); } printk(" elf loaded\n"); return 0;}__attribute_used__ void dump_vpe(struct vpe * v){ struct tc *t; settc(v->minor); 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);}static void cleanup_tc(struct tc *tc){ int tmp; /* Put MVPE's into 'configuration state' */ set_c0_mvpcontrol(MVPCONTROL_VPC); settc(tc->index); 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); /* bind it to anything other than VPE1 */ write_tc_c0_tcbind(read_tc_c0_tcbind() & ~TCBIND_CURVPE); // | TCBIND_CURVPE clear_c0_mvpcontrol(MVPCONTROL_VPC);}static int getcwd(char *buff, int size){ mm_segment_t old_fs; int ret; old_fs = get_fs(); set_fs(KERNEL_DS); ret = sys_getcwd(buff,size); set_fs(old_fs); return ret;}/* checks VPE is unused and gets ready to load program */static int vpe_open(struct inode *inode, struct file *filp){ int minor, ret; struct vpe *v; struct vpe_notifications *not; /* assume only 1 device at the mo. */ if ((minor = iminor(inode)) != 1) { printk(KERN_WARNING "VPE loader: only vpe1 is supported\n"); return -ENODEV; } if ((v = get_vpe(minor)) == NULL) { printk(KERN_WARNING "VPE loader: unable to get vpe\n"); return -ENODEV; } if (v->state != VPE_STATE_UNUSED) { dvpe(); printk(KERN_DEBUG "VPE loader: tc in use dumping regs\n"); dump_tc(get_tc(minor)); list_for_each_entry(not, &v->notify, list) { not->stop(minor); } release_progmem(v->load_addr); cleanup_tc(get_tc(minor)); } // 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; v->uid = filp->f_uid; v->gid = filp->f_gid;#ifdef CONFIG_MIPS_APSP_KSPD /* get kspd to tell us when a syscall_exit happens */ if (!kspd_events_reqd) { kspd_notify(&kspd_events); kspd_events_reqd++; }#endif v->cwd[0] = 0; ret = getcwd(v->cwd, VPE_PATH_MAX); if (ret < 0) printk(KERN_WARNING "VPE loader: open, getcwd returned %d\n", ret); v->shared_ptr = NULL; v->__start = 0; return 0;}static int vpe_release(struct inode *inode, struct file *filp){ int minor, ret = 0; struct vpe *v; Elf_Ehdr *hdr; minor = iminor(inode); 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 loader: ELF load failed.\n"); ret = -ENOEXEC; } } else { printk(KERN_WARNING "VPE loader: only elf files are supported\n"); ret = -ENOEXEC; } /* It's good to be able to run the SP and if it chokes have a look at the /dev/rt?. But if we reset the pointer to the shared struct we loose what has happened. So perhaps if garbage is sent to the vpe device, use it as a trigger for the reset. Hopefully a nice executable will be along shortly. */ if (ret < 0) v->shared_ptr = NULL; // 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 = iminor(file->f_dentry->d_inode); if ((v = get_vpe(minor)) == NULL) return -ENODEV; if (v->pbuffer == NULL) { printk(KERN_ERR "VPE loader: no buffer for program\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) 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) return NULL; return v->shared_ptr;}EXPORT_SYMBOL(vpe_get_shared);int vpe_getuid(int index){ struct vpe *v; if ((v = get_vpe(index)) == NULL) return -1; return v->uid;}EXPORT_SYMBOL(vpe_getuid);int vpe_getgid(int index){ struct vpe *v; if ((v = get_vpe(index)) == NULL) return -1; return v->gid;}EXPORT_SYMBOL(vpe_getgid);int vpe_notify(int index, struct vpe_notifications *notify){ struct vpe *v; if ((v = get_vpe(index)) == NULL) return -1; list_add(¬ify->list, &v->notify); return 0;}EXPORT_SYMBOL(vpe_notify);char *vpe_getcwd(int index){ struct vpe *v; if ((v = get_vpe(index)) == NULL) return NULL; return v->cwd;}EXPORT_SYMBOL(vpe_getcwd);#ifdef CONFIG_MIPS_APSP_KSPDstatic void kspd_sp_exit( int sp_id){ cleanup_tc(get_tc(sp_id));}#endifstatic 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; } major = register_chrdev(0, module_name, &vpe_fops); if (major < 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; } /* add the tc to the list of this vpe's tc's. */ list_add(&t->tc, &v->tc); /* 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; settc(i); /* Any TC that is bound to VPE0 gets left as is - in case we are running SMTC on VPE0. A TC that is bound to any other VPE gets bound to VPE0, ideally I'd like to make it homeless but it doesn't appear to let me bind a TC to a non-existent VPE. Which is perfectly reasonable. The (un)bound state is visible to an EJTAG probe so may notify GDB... */ if (((tmp = read_tc_c0_tcbind()) & TCBIND_CURVPE)) { /* tc is bound >vpe0 */ write_tc_c0_tcbind(tmp & ~TCBIND_CURVPE); t->pvpe = get_vpe(0); /* set the parent vpe */ } tmp = read_tc_c0_tcstatus(); /* mark not activated 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);#ifdef CONFIG_MIPS_APSP_KSPD kspd_events.kspd_sp_exit = kspd_sp_exit;#endif 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 Oldham, MIPS Technologies, Inc.");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -