vpe.c

来自「linux 内核源代码」· C语言 代码 · 共 1,594 行 · 第 1/3 页

C
1,594
字号
	[R_MIPS_PC16] = "MIPS_PC16"};int apply_relocations(Elf32_Shdr *sechdrs,		      const char *strtab,		      unsigned int symindex,		      unsigned int relsec,		      struct module *me){	Elf32_Rel *rel = (void *) sechdrs[relsec].sh_addr;	Elf32_Sym *sym;	uint32_t *location;	unsigned int i;	Elf32_Addr v;	int res;	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {		Elf32_Word r_info = rel[i].r_info;		/* This is where to make the change */		location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr			+ rel[i].r_offset;		/* This is the symbol it is referring to */		sym = (Elf32_Sym *)sechdrs[symindex].sh_addr			+ ELF32_R_SYM(r_info);		if (!sym->st_value) {			printk(KERN_DEBUG "%s: undefined weak symbol %s\n",			       me->name, strtab + sym->st_name);			/* just print the warning, dont barf */		}		v = sym->st_value;		res = reloc_handlers[ELF32_R_TYPE(r_info)](me, location, v);		if( res ) {			char *r = rstrs[ELF32_R_TYPE(r_info)];		    	printk(KERN_WARNING "VPE loader: .text+0x%x "			       "relocation type %s for symbol \"%s\" failed\n",			       rel[i].r_offset, r ? r : "UNKNOWN",			       strtab + sym->st_name);			return res;		}	}	return 0;}void save_gp_address(unsigned int secbase, unsigned int rel){	gp_addr = secbase + rel;	gp_offs = gp_addr - (secbase & 0xffff0000);}/* end module-elf32.c *//* Change all symbols so that sh_value encodes the pointer directly. */static void simplify_symbols(Elf_Shdr * sechdrs,			    unsigned int symindex,			    const char *strtab,			    const char *secstrings,			    unsigned int nsecs, struct module *mod){	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;	unsigned long secbase, bssbase = 0;	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);	int size;	/* find the .bss section for COMMON symbols */	for (i = 0; i < nsecs; i++) {		if (strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) == 0) {			bssbase = sechdrs[i].sh_addr;			break;		}	}	for (i = 1; i < n; i++) {		switch (sym[i].st_shndx) {		case SHN_COMMON:			/* Allocate space for the symbol in the .bss section.			   st_value is currently size.			   We want it to have the address of the symbol. */			size = sym[i].st_value;			sym[i].st_value = bssbase;			bssbase += size;			break;		case SHN_ABS:			/* Don't need to do anything */			break;		case SHN_UNDEF:			/* ret = -ENOENT; */			break;		case SHN_MIPS_SCOMMON:			printk(KERN_DEBUG "simplify_symbols: ignoring SHN_MIPS_SCOMMON "			       "symbol <%s> st_shndx %d\n", strtab + sym[i].st_name,			       sym[i].st_shndx);			// .sbss section			break;		default:			secbase = sechdrs[sym[i].st_shndx].sh_addr;			if (strncmp(strtab + sym[i].st_name, "_gp", 3) == 0) {				save_gp_address(secbase, sym[i].st_value);			}			sym[i].st_value += secbase;			break;		}	}}#ifdef DEBUG_ELFLOADERstatic void dump_elfsymbols(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);	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);	}}#endif/* We are prepared so configure and start the VPE... */static int vpe_run(struct vpe * v){	unsigned long flags, val, dmt_flag;	struct vpe_notifications *n;	unsigned int vpeflags;	struct tc *t;	/* check we are the Master VPE */	local_irq_save(flags);	val = read_c0_vpeconf0();	if (!(val & VPECONF0_MVP)) {		printk(KERN_WARNING		       "VPE loader: only Master VPE's are allowed to configure MT\n");		local_irq_restore(flags);		return -1;	}	dmt_flag = dmt();	vpeflags = dvpe();	if (!list_empty(&v->tc)) {		if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {			evpe(vpeflags);			emt(dmt_flag);			local_irq_restore(flags);			printk(KERN_WARNING			       "VPE loader: TC %d is already in use.\n",                               t->index);			return -ENOEXEC;		}	} else {		evpe(vpeflags);		emt(dmt_flag);		local_irq_restore(flags);		printk(KERN_WARNING		       "VPE loader: No TC's associated with VPE %d\n",		       v->minor);		return -ENOEXEC;	}	/* Put MVPE's into 'configuration state' */	set_c0_mvpcontrol(MVPCONTROL_VPC);	settc(t->index);	/* should check it is halted, and not activated */	if ((read_tc_c0_tcstatus() & TCSTATUS_A) || !(read_tc_c0_tchalt() & TCHALT_H)) {		evpe(vpeflags);		emt(dmt_flag);		local_irq_restore(flags);		printk(KERN_WARNING "VPE loader: TC %d is already active!\n",		       t->index);		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_tc_c0_tccontext((unsigned long)0);	/*	 * 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);	/*	 * The sde-kit passes 'memsize' to __start in $a3, so set something	 * here...  Or set $a3 to zero and define DFLT_STACK_SIZE and	 * DFLT_HEAP_SIZE when you compile your program	 */	mttgpr(6, v->ntcs);	mttgpr(7, physical_memsize);	/* set up VPE1 */	/*	 * bind the TC to VPE 1 as late as possible so we only have the final	 * VPE registers to set up, and so an EJTAG probe can trigger on it	 */	write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | 1);	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~(VPECONF0_VPA));	back_to_back_c0_hazard();	/* Set up the XTC bit in vpeconf0 to point at our tc */	write_vpe_c0_vpeconf0( (read_vpe_c0_vpeconf0() & ~(VPECONF0_XTC))	                      | (t->index << VPECONF0_XTC_SHIFT));	back_to_back_c0_hazard();	/* enable this VPE */	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA);	/* clear out any left overs from a previous program */	write_vpe_c0_status(0);	write_vpe_c0_cause(0);	/* take system out of configuration state */	clear_c0_mvpcontrol(MVPCONTROL_VPC);#ifdef CONFIG_SMP	evpe(EVPE_ENABLE);#else	evpe(vpeflags);#endif	emt(dmt_flag);	local_irq_restore(flags);	list_for_each_entry(n, &v->notify, list)		n->start(minor);	return 0;}static int 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;		}	}	if ( (v->__start == 0) || (v->shared_ptr == NULL))		return -1;	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. */static 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, relocate = 0;	struct module mod;	// so we can re-use the relocations code	memset(&mod, 0, sizeof(struct module));	strcpy(mod.name, "VPE loader");	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 && hdr->e_type != ET_EXEC)	    || !elf_check_arch(hdr)	    || hdr->e_shentsize != sizeof(*sechdrs)) {		printk(KERN_WARNING		       "VPE loader: program wrong arch or weird elf version\n");		return -ENOEXEC;	}	if (hdr->e_type == ET_REL)		relocate = 1;	if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) {		printk(KERN_ERR "VPE loader: 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;	if (relocate) {		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 loader: loading to %p\n", v->load_addr);	if (relocate) {		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;			printk(KERN_DEBUG " section sh_name %s sh_addr 0x%x\n",			       secstrings + sechdrs[i].sh_name, sechdrs[i].sh_addr);		} 		/* Fix up syms, so that st_value is a pointer to location. */ 		simplify_symbols(sechdrs, symindex, strtab, secstrings, 				 hdr->e_shnum, &mod); 		/* 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) 				return err;  		}  	} else {		struct elf_phdr *phdr = (struct elf_phdr *) ((char *)hdr + hdr->e_phoff);		for (i = 0; i < hdr->e_phnum; i++) {			if (phdr->p_type != PT_LOAD)				continue;			memcpy((void *)phdr->p_paddr, (char *)hdr + phdr->p_offset, phdr->p_filesz);			memset((void *)phdr->p_paddr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz);			phdr++;		}		for (i = 0; i < hdr->e_shnum; i++) { 			/* 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; 				/* mark the symtab's address for when we try to find the 				   magic symbols */ 				sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset; 			}		}	}	/* 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) {		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;}static void cleanup_tc(struct tc *tc){	unsigned long flags;	unsigned int mtflags, vpflags;	int tmp;	local_irq_save(flags);	mtflags = dmt();	vpflags = dvpe();	/* 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);	mips_ihb();	/* bind it to anything other than VPE1 *///	write_tc_c0_tcbind(read_tc_c0_tcbind() & ~TCBIND_CURVPE); // | TCBIND_CURVPE	clear_c0_mvpcontrol(MVPCONTROL_VPC);	evpe(vpflags);	emt(mtflags);	local_irq_restore(flags);}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){	enum vpe_state state;	struct vpe_notifications *not;	struct vpe *v;	int ret;	if (minor != iminor(inode)) {		/* assume only 1 device at the moment. */		printk(KERN_WARNING "VPE loader: only vpe1 is supported\n");		return -ENODEV;	}	if ((v = get_vpe(tclimit)) == NULL) {		printk(KERN_WARNING "VPE loader: unable to get vpe\n");		return -ENODEV;	}	state = xchg(&v->state, VPE_STATE_INUSE);	if (state != VPE_STATE_UNUSED) {		printk(KERN_DEBUG "VPE loader: tc in use dumping regs\n");		list_for_each_entry(not, &v->notify, list) {			not->stop(tclimit);		}		release_progmem(v->load_addr);		cleanup_tc(get_tc(tclimit));	}	/* this of-course trashes what was there before... */	v->pbuffer = vmalloc(P_SIZE);

⌨️ 快捷键说明

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