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(&notify->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 + -
显示快捷键?