vpe.c
来自「linux 内核源代码」· C语言 代码 · 共 1,594 行 · 第 1/3 页
C
1,594 行
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){ struct vpe *v; Elf_Ehdr *hdr; int ret = 0; v = get_vpe(tclimit); if (v == NULL) return -ENODEV; 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){ size_t ret = count; struct vpe *v; if (iminor(file->f_path.dentry->d_inode) != minor) return -ENODEV; v = get_vpe(tclimit); if (v == 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 const 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); /* halt the TC */ write_tc_c0_tchalt(TCHALT_H); mips_ihb(); /* mark the TC unallocated */ write_tc_c0_tcstatus(read_tc_c0_tcstatus() & ~TCSTATUS_A); 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 ssize_t store_kill(struct device *dev, struct device_attribute *attr, const char *buf, size_t len){ struct vpe *vpe = get_vpe(tclimit); struct vpe_notifications *not; list_for_each_entry(not, &vpe->notify, list) { not->stop(tclimit); } release_progmem(vpe->load_addr); cleanup_tc(get_tc(tclimit)); vpe_stop(vpe); vpe_free(vpe); return len;}static ssize_t show_ntcs(struct device *cd, struct device_attribute *attr, char *buf){ struct vpe *vpe = get_vpe(tclimit); return sprintf(buf, "%d\n", vpe->ntcs);}static ssize_t store_ntcs(struct device *dev, struct device_attribute *attr, const char *buf, size_t len){ struct vpe *vpe = get_vpe(tclimit); unsigned long new; char *endp; new = simple_strtoul(buf, &endp, 0); if (endp == buf) goto out_einval; if (new == 0 || new > (hw_tcs - tclimit)) goto out_einval; vpe->ntcs = new; return len;out_einval: return -EINVAL;;}static struct device_attribute vpe_class_attributes[] = { __ATTR(kill, S_IWUSR, NULL, store_kill), __ATTR(ntcs, S_IRUGO | S_IWUSR, show_ntcs, store_ntcs), {}};static void vpe_device_release(struct device *cd){ kfree(cd);}struct class vpe_class = { .name = "vpe", .owner = THIS_MODULE, .dev_release = vpe_device_release, .dev_attrs = vpe_class_attributes,};struct device vpe_device;static int __init vpe_module_init(void){ unsigned int mtflags, vpflags; unsigned long flags, val; struct vpe *v = NULL; struct tc *t; int tc, err; if (!cpu_has_mipsmt) { printk("VPE loader: not a MIPS MT capable processor\n"); return -ENODEV; } if (vpelimit == 0) { printk(KERN_WARNING "No VPEs reserved for AP/SP, not " "initializing VPE loader.\nPass maxvpes=<n> argument as " "kernel argument\n"); return -ENODEV; } if (tclimit == 0) { printk(KERN_WARNING "No TCs reserved for AP/SP, not " "initializing VPE loader.\nPass maxtcs=<n> argument as " "kernel argument\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; } err = class_register(&vpe_class); if (err) { printk(KERN_ERR "vpe_class registration failed\n"); goto out_chrdev; } device_initialize(&vpe_device); vpe_device.class = &vpe_class, vpe_device.parent = NULL, strlcpy(vpe_device.bus_id, "vpe1", BUS_ID_SIZE); vpe_device.devt = MKDEV(major, minor); err = device_add(&vpe_device); if (err) { printk(KERN_ERR "Adding vpe_device failed\n"); goto out_class; } local_irq_save(flags); mtflags = dmt(); vpflags = dvpe(); /* Put MVPE's into 'configuration state' */ set_c0_mvpcontrol(MVPCONTROL_VPC); /* dump_mtregs(); */ val = read_c0_mvpconf0(); hw_tcs = (val & MVPCONF0_PTC) + 1; hw_vpes = ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1; for (tc = tclimit; tc < hw_tcs; tc++) { /* * Must re-enable multithreading temporarily or in case we * reschedule send IPIs or similar we might hang. */ clear_c0_mvpcontrol(MVPCONTROL_VPC); evpe(vpflags); emt(mtflags); local_irq_restore(flags); t = alloc_tc(tc); if (!t) { err = -ENOMEM; goto out; } local_irq_save(flags); mtflags = dmt(); vpflags = dvpe(); set_c0_mvpcontrol(MVPCONTROL_VPC); /* VPE's */ if (tc < hw_tcs) { settc(tc); if ((v = alloc_vpe(tc)) == NULL) { printk(KERN_WARNING "VPE: unable to allocate VPE\n"); goto out_reenable; } v->ntcs = hw_tcs - tclimit; /* add the tc to the list of this vpe's tc's. */ list_add(&t->tc, &v->tc); /* deactivate all but vpe0 */ if (tc >= tclimit) { 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 (tc >= vpelimit) { /* * 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 (tc >= tclimit) { unsigned long tmp; settc(tc); /* 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 */ } /* halt the TC */ write_tc_c0_tchalt(TCHALT_H); mips_ihb(); 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); } }out_reenable: /* release config state */ clear_c0_mvpcontrol(MVPCONTROL_VPC); evpe(vpflags); emt(mtflags); local_irq_restore(flags);#ifdef CONFIG_MIPS_APSP_KSPD kspd_events.kspd_sp_exit = kspd_sp_exit;#endif return 0;out_class: class_unregister(&vpe_class);out_chrdev: unregister_chrdev(major, module_name);out: return err;}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); } } device_del(&vpe_device); 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 + =
减小字号Ctrl + -
显示快捷键?