⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 vpe.c

📁 LINUX 2.6.17.4的源码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Copyright (C) 2004, 2005 MIPS Technologies, Inc.  All rights reserved. * *  This program is free software; you can distribute it and/or modify it *  under the terms of the GNU General Public License (Version 2) as *  published by the Free Software Foundation. * *  This program is distributed in the hope it will be useful, but WITHOUT *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License *  for more details. * *  You should have received a copy of the GNU General Public License along *  with this program; if not, write to the Free Software Foundation, Inc., *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA. *//* * VPE support module * * Provides support for loading a MIPS SP program on VPE1. * The SP enviroment is rather simple, no tlb's.  It needs to be relocatable * (or partially linked). You should initialise your stack in the startup * code. This loader looks for the symbol __start and sets up * execution to resume from there. The MIPS SDE kit contains suitable examples. * * To load and run, simply cat a SP 'program file' to /dev/vpe1. * i.e cat spapp >/dev/vpe1. */#include <linux/config.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/fs.h>#include <linux/init.h>#include <asm/uaccess.h>#include <linux/slab.h>#include <linux/list.h>#include <linux/vmalloc.h>#include <linux/elf.h>#include <linux/seq_file.h>#include <linux/syscalls.h>#include <linux/moduleloader.h>#include <linux/interrupt.h>#include <linux/poll.h>#include <linux/bootmem.h>#include <asm/mipsregs.h>#include <asm/mipsmtregs.h>#include <asm/cacheflush.h>#include <asm/atomic.h>#include <asm/cpu.h>#include <asm/processor.h>#include <asm/system.h>#include <asm/vpe.h>#include <asm/kspd.h>typedef void *vpe_handle;#ifndef ARCH_SHF_SMALL#define ARCH_SHF_SMALL 0#endif/* If this is set, the section belongs in the init part of the module */#define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))static char module_name[] = "vpe";static int major;#ifdef CONFIG_MIPS_APSP_KSPD static struct kspd_notifications kspd_events;static int kspd_events_reqd = 0;#endif/* grab the likely amount of memory we will need. */#ifdef CONFIG_MIPS_VPE_LOADER_TOM#define P_SIZE (2 * 1024 * 1024)#else/* add an overhead to the max kmalloc size for non-striped symbols/etc */#define P_SIZE (256 * 1024)#endifextern unsigned long physical_memsize;#define MAX_VPES 16#define VPE_PATH_MAX 256enum vpe_state {	VPE_STATE_UNUSED = 0,	VPE_STATE_INUSE,	VPE_STATE_RUNNING};enum tc_state {	TC_STATE_UNUSED = 0,	TC_STATE_INUSE,	TC_STATE_RUNNING,	TC_STATE_DYNAMIC};struct vpe {	enum vpe_state state;	/* (device) minor associated with this vpe */	int minor;	/* elfloader stuff */	void *load_addr;	unsigned long len;	char *pbuffer;	unsigned long plen;	unsigned int uid, gid;	char cwd[VPE_PATH_MAX];	unsigned long __start;	/* tc's associated with this vpe */	struct list_head tc;	/* The list of vpe's */	struct list_head list;	/* shared symbol address */	void *shared_ptr;	/* the list of who wants to know when something major happens */	struct list_head notify;};struct tc {	enum tc_state state;	int index;	/* parent VPE */	struct vpe *pvpe;	/* The list of TC's with this VPE */	struct list_head tc;	/* The global list of tc's */	struct list_head list;};struct vpecontrol_ {	/* Virtual processing elements */	struct list_head vpe_list;	/* Thread contexts */	struct list_head tc_list;} vpecontrol;static void release_progmem(void *ptr);/* static __attribute_used__ void dump_vpe(struct vpe * v); */extern void save_gp_address(unsigned int secbase, unsigned int rel);/* get the vpe associated with this minor */struct vpe *get_vpe(int minor){	struct vpe *v;	if (!cpu_has_mipsmt)		return NULL;	list_for_each_entry(v, &vpecontrol.vpe_list, list) {		if (v->minor == minor)			return v;	}	return NULL;}/* get the vpe associated with this minor */struct tc *get_tc(int index){	struct tc *t;	list_for_each_entry(t, &vpecontrol.tc_list, list) {		if (t->index == index)			return t;	}	return NULL;}struct tc *get_tc_unused(void){	struct tc *t;	list_for_each_entry(t, &vpecontrol.tc_list, list) {		if (t->state == TC_STATE_UNUSED)			return t;	}	return NULL;}/* allocate a vpe and associate it with this minor (or index) */struct vpe *alloc_vpe(int minor){	struct vpe *v;	if ((v = kzalloc(sizeof(struct vpe), GFP_KERNEL)) == NULL) {		return NULL;	}	INIT_LIST_HEAD(&v->tc);	list_add_tail(&v->list, &vpecontrol.vpe_list);	INIT_LIST_HEAD(&v->notify);	v->minor = minor;	return v;}/* allocate a tc. At startup only tc0 is running, all other can be halted. */struct tc *alloc_tc(int index){	struct tc *t;	if ((t = kzalloc(sizeof(struct tc), GFP_KERNEL)) == NULL) {		return NULL;	}	INIT_LIST_HEAD(&t->tc);	list_add_tail(&t->list, &vpecontrol.tc_list);	t->index = index;	return t;}/* clean up and free everything */void release_vpe(struct vpe *v){	list_del(&v->list);	if (v->load_addr)		release_progmem(v);	kfree(v);}void dump_mtregs(void){	unsigned long val;	val = read_c0_config3();	printk("config3 0x%lx MT %ld\n", val,	       (val & CONFIG3_MT) >> CONFIG3_MT_SHIFT);	val = read_c0_mvpcontrol();	printk("MVPControl 0x%lx, STLB %ld VPC %ld EVP %ld\n", val,	       (val & MVPCONTROL_STLB) >> MVPCONTROL_STLB_SHIFT,	       (val & MVPCONTROL_VPC) >> MVPCONTROL_VPC_SHIFT,	       (val & MVPCONTROL_EVP));	val = read_c0_mvpconf0();	printk("mvpconf0 0x%lx, PVPE %ld PTC %ld M %ld\n", val,	       (val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT,	       val & MVPCONF0_PTC, (val & MVPCONF0_M) >> MVPCONF0_M_SHIFT);}/* Find some VPE program space  */static void *alloc_progmem(unsigned long len){#ifdef CONFIG_MIPS_VPE_LOADER_TOM	/* this means you must tell linux to use less memory than you physically have */	return pfn_to_kaddr(max_pfn);#else	// simple grab some mem for now	return kmalloc(len, GFP_KERNEL);#endif}static void release_progmem(void *ptr){#ifndef CONFIG_MIPS_VPE_LOADER_TOM	kfree(ptr);#endif}/* Update size with this section: return offset. */static long get_offset(unsigned long *size, Elf_Shdr * sechdr){	long ret;	ret = ALIGN(*size, sechdr->sh_addralign ? : 1);	*size = ret + sechdr->sh_size;	return ret;}/* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld   might -- code, read-only data, read-write data, small data.  Tally   sizes, and place the offsets into sh_entsize fields: high bit means it   belongs in init. */static void layout_sections(struct module *mod, const Elf_Ehdr * hdr,			    Elf_Shdr * sechdrs, const char *secstrings){	static unsigned long const masks[][2] = {		/* NOTE: all executable code must be the first section		 * in this array; otherwise modify the text_size		 * finder in the two loops below */		{SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL},		{SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL},		{SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL},		{ARCH_SHF_SMALL | SHF_ALLOC, 0}	};	unsigned int m, i;	for (i = 0; i < hdr->e_shnum; i++)		sechdrs[i].sh_entsize = ~0UL;	for (m = 0; m < ARRAY_SIZE(masks); ++m) {		for (i = 0; i < hdr->e_shnum; ++i) {			Elf_Shdr *s = &sechdrs[i];			//  || strncmp(secstrings + s->sh_name, ".init", 5) == 0)			if ((s->sh_flags & masks[m][0]) != masks[m][0]			    || (s->sh_flags & masks[m][1])			    || s->sh_entsize != ~0UL)				continue;			s->sh_entsize = get_offset(&mod->core_size, s);		}		if (m == 0)			mod->core_text_size = mod->core_size;	}}/* from module-elf32.c, but subverted a little */struct mips_hi16 {	struct mips_hi16 *next;	Elf32_Addr *addr;	Elf32_Addr value;};static struct mips_hi16 *mips_hi16_list;static unsigned int gp_offs, gp_addr;static int apply_r_mips_none(struct module *me, uint32_t *location,			     Elf32_Addr v){	return 0;}static int apply_r_mips_gprel16(struct module *me, uint32_t *location,				Elf32_Addr v){	int rel;	if( !(*location & 0xffff) ) {		rel = (int)v - gp_addr;	}	else {		/* .sbss + gp(relative) + offset */		/* kludge! */		rel =  (int)(short)((int)v + gp_offs +				    (int)(short)(*location & 0xffff) - gp_addr);	}	if( (rel > 32768) || (rel < -32768) ) {		printk(KERN_DEBUG "VPE loader: apply_r_mips_gprel16: "		       "relative address 0x%x out of range of gp register\n",		       rel);		return -ENOEXEC;	}	*location = (*location & 0xffff0000) | (rel & 0xffff);	return 0;}static int apply_r_mips_pc16(struct module *me, uint32_t *location,			     Elf32_Addr v){	int rel;	rel = (((unsigned int)v - (unsigned int)location));	rel >>= 2;		// because the offset is in _instructions_ not bytes.	rel -= 1;		// and one instruction less due to the branch delay slot.	if( (rel > 32768) || (rel < -32768) ) {		printk(KERN_DEBUG "VPE loader: " 		       "apply_r_mips_pc16: relative address out of range 0x%x\n", rel);		return -ENOEXEC;	}	*location = (*location & 0xffff0000) | (rel & 0xffff);	return 0;}static int apply_r_mips_32(struct module *me, uint32_t *location,			   Elf32_Addr v){	*location += v;	return 0;}static int apply_r_mips_26(struct module *me, uint32_t *location,			   Elf32_Addr v){	if (v % 4) {		printk(KERN_DEBUG "VPE loader: apply_r_mips_26 "		       " unaligned relocation\n");		return -ENOEXEC;	}/* * Not desperately convinced this is a good check of an overflow condition * anyway. But it gets in the way of handling undefined weak symbols which * we want to set to zero. * if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) { * printk(KERN_ERR * "module %s: relocation overflow\n", * me->name); * return -ENOEXEC; * } */	*location = (*location & ~0x03ffffff) |		((*location + (v >> 2)) & 0x03ffffff);	return 0;}static int apply_r_mips_hi16(struct module *me, uint32_t *location,			     Elf32_Addr v){	struct mips_hi16 *n;	/*	 * We cannot relocate this one now because we don't know the value of	 * the carry we need to add.  Save the information, and let LO16 do the	 * actual relocation.	 */	n = kmalloc(sizeof *n, GFP_KERNEL);	if (!n)		return -ENOMEM;	n->addr = location;	n->value = v;	n->next = mips_hi16_list;	mips_hi16_list = n;	return 0;}static int apply_r_mips_lo16(struct module *me, uint32_t *location,			     Elf32_Addr v){	unsigned long insnlo = *location;	Elf32_Addr val, vallo;	/* Sign extend the addend we extract from the lo insn.  */	vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;	if (mips_hi16_list != NULL) {		struct mips_hi16 *l;		l = mips_hi16_list;		while (l != NULL) {			struct mips_hi16 *next;			unsigned long insn;			/*			 * The value for the HI16 had best be the same.			 */ 			if (v != l->value) {				printk(KERN_DEBUG "VPE loader: "				       "apply_r_mips_lo16/hi16: 	"				       "inconsistent value information\n");				return -ENOEXEC;			}			/*			 * Do the HI16 relocation.  Note that we actually don't			 * need to know anything about the LO16 itself, except			 * where to find the low 16 bits of the addend needed			 * by the LO16.			 */			insn = *l->addr;			val = ((insn & 0xffff) << 16) + vallo;			val += v;			/*			 * Account for the sign extension that will happen in			 * the low bits.			 */			val = ((val >> 16) + ((val & 0x8000) != 0)) & 0xffff;			insn = (insn & ~0xffff) | val;			*l->addr = insn;			next = l->next;			kfree(l);			l = next;		}		mips_hi16_list = NULL;	}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -