diff options
Diffstat (limited to 'arch/mips/kernel/vpe.c')
| -rw-r--r-- | arch/mips/kernel/vpe.c | 1022 | 
1 files changed, 163 insertions, 859 deletions
diff --git a/arch/mips/kernel/vpe.c b/arch/mips/kernel/vpe.c index 3eb3cde2f66..11da314565c 100644 --- a/arch/mips/kernel/vpe.c +++ b/arch/mips/kernel/vpe.c @@ -1,38 +1,22 @@  /* - * 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 + * This file is subject to the terms and conditions of the GNU General Public + * License.  See the file "COPYING" in the main directory of this archive + * for more details.   * - * 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. + * Copyright (C) 2004, 2005 MIPS Technologies, Inc.  All rights reserved. + * Copyright (C) 2013 Imagination Technologies Ltd.   * - * To load and run, simply cat a SP 'program file' to /dev/vpe1. - * i.e cat spapp >/dev/vpe1. + * VPE spport module for loading a MIPS SP program into VPE1. The SP + * environment is rather simple since there are no TLBs. It needs + * to be relocatable (or partiall linked). Initialize your stack in + * the startup-code. The loader looks for the symbol __start and sets + * up the execution to resume from there. To load and run, simply do + * a cat SP 'binary' to the /dev/vpe1 device.   */  #include <linux/kernel.h>  #include <linux/device.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> @@ -46,15 +30,10 @@  #include <asm/mipsregs.h>  #include <asm/mipsmtregs.h>  #include <asm/cacheflush.h> -#include <asm/atomic.h> -#include <asm/cpu.h> +#include <linux/atomic.h>  #include <asm/mips_mt.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 @@ -63,101 +42,15 @@ typedef void *vpe_handle;  /* If this is set, the section belongs in the init part of the module */  #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1)) -/* - * The number of TCs and VPEs physically available on the core - */ -static int hw_tcs, hw_vpes; -static char module_name[] = "vpe"; -static int major; -static const int minor = 1;	/* fixed for now  */ - -#ifdef CONFIG_MIPS_APSP_KSPD -static struct kspd_notifications kspd_events; -static int kspd_events_reqd; -#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) -#endif - -extern unsigned long physical_memsize; - -#define MAX_VPES 16 -#define VPE_PATH_MAX 256 - -enum 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; - -	unsigned int ntcs; -}; - -struct tc { -	enum tc_state state; -	int index; - -	struct vpe *pvpe;	/* parent VPE */ -	struct list_head tc;	/* The list of TC's with this VPE */ -	struct list_head list;	/* The global list of tc's */ -}; - -struct { -	spinlock_t vpe_list_lock; -	struct list_head vpe_list;	/* Virtual processing elements */ -	spinlock_t tc_list_lock; -	struct list_head tc_list;	/* Thread contexts */ -} vpecontrol = { -	.vpe_list_lock	= SPIN_LOCK_UNLOCKED, +struct vpe_control vpecontrol = { +	.vpe_list_lock	= __SPIN_LOCK_UNLOCKED(vpe_list_lock),  	.vpe_list	= LIST_HEAD_INIT(vpecontrol.vpe_list), -	.tc_list_lock	= SPIN_LOCK_UNLOCKED, +	.tc_list_lock	= __SPIN_LOCK_UNLOCKED(tc_list_lock),  	.tc_list	= LIST_HEAD_INIT(vpecontrol.tc_list)  }; -static void release_progmem(void *ptr); -  /* get the vpe associated with this minor */ -static struct vpe *get_vpe(int minor) +struct vpe *get_vpe(int minor)  {  	struct vpe *res, *v; @@ -167,7 +60,7 @@ static struct vpe *get_vpe(int minor)  	res = NULL;  	spin_lock(&vpecontrol.vpe_list_lock);  	list_for_each_entry(v, &vpecontrol.vpe_list, list) { -		if (v->minor == minor) { +		if (v->minor == VPE_MODULE_MINOR) {  			res = v;  			break;  		} @@ -178,7 +71,7 @@ static struct vpe *get_vpe(int minor)  }  /* get the vpe associated with this minor */ -static struct tc *get_tc(int index) +struct tc *get_tc(int index)  {  	struct tc *res, *t; @@ -192,16 +85,17 @@ static struct tc *get_tc(int index)  	}  	spin_unlock(&vpecontrol.tc_list_lock); -	return NULL; +	return res;  }  /* allocate a vpe and associate it with this minor (or index) */ -static struct vpe *alloc_vpe(int minor) +struct vpe *alloc_vpe(int minor)  {  	struct vpe *v; -	if ((v = kzalloc(sizeof(struct vpe), GFP_KERNEL)) == NULL) -		return NULL; +	v = kzalloc(sizeof(struct vpe), GFP_KERNEL); +	if (v == NULL) +		goto out;  	INIT_LIST_HEAD(&v->tc);  	spin_lock(&vpecontrol.vpe_list_lock); @@ -209,17 +103,19 @@ static struct vpe *alloc_vpe(int minor)  	spin_unlock(&vpecontrol.vpe_list_lock);  	INIT_LIST_HEAD(&v->notify); -	v->minor = minor; +	v->minor = VPE_MODULE_MINOR; +out:  	return v;  }  /* allocate a tc. At startup only tc0 is running, all other can be halted. */ -static struct tc *alloc_tc(int index) +struct tc *alloc_tc(int index)  {  	struct tc *tc; -	if ((tc = kzalloc(sizeof(struct tc), GFP_KERNEL)) == NULL) +	tc = kzalloc(sizeof(struct tc), GFP_KERNEL); +	if (tc == NULL)  		goto out;  	INIT_LIST_HEAD(&tc->tc); @@ -234,7 +130,7 @@ out:  }  /* clean up and free everything */ -static void release_vpe(struct vpe *v) +void release_vpe(struct vpe *v)  {  	list_del(&v->list);  	if (v->load_addr) @@ -242,28 +138,8 @@ static void release_vpe(struct vpe *v)  	kfree(v);  } -static void __maybe_unused 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) +/* Find some VPE program space */ +void *alloc_progmem(unsigned long len)  {  	void *addr; @@ -282,7 +158,7 @@ static void *alloc_progmem(unsigned long len)  	return addr;  } -static void release_progmem(void *ptr) +void release_progmem(void *ptr)  {  #ifndef CONFIG_MIPS_VPE_LOADER_TOM  	kfree(ptr); @@ -290,7 +166,7 @@ static void release_progmem(void *ptr)  }  /* Update size with this section: return offset. */ -static long get_offset(unsigned long *size, Elf_Shdr * sechdr) +static long get_offset(unsigned long *size, Elf_Shdr *sechdr)  {  	long ret; @@ -300,11 +176,11 @@ static long get_offset(unsigned long *size, Elf_Shdr * sechdr)  }  /* 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 +   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 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 @@ -324,7 +200,6 @@ static void layout_sections(struct module *mod, const Elf_Ehdr * hdr,  		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) @@ -339,7 +214,6 @@ static void layout_sections(struct module *mod, const Elf_Ehdr * hdr,  	}  } -  /* from module-elf32.c, but subverted a little */  struct mips_hi16 { @@ -362,20 +236,18 @@ static int apply_r_mips_gprel16(struct module *me, uint32_t *location,  {  	int rel; -	if( !(*location & 0xffff) ) { +	if (!(*location & 0xffff)) {  		rel = (int)v - gp_addr; -	} -	else { +	} 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); +	if ((rel > 32768) || (rel < -32768)) { +		pr_debug("VPE loader: apply_r_mips_gprel16: relative address 0x%x out of range of gp register\n", +			 rel);  		return -ENOEXEC;  	} @@ -389,12 +261,12 @@ static int apply_r_mips_pc16(struct module *me, uint32_t *location,  {  	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. +	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); +	if ((rel > 32768) || (rel < -32768)) { +		pr_debug("VPE loader: apply_r_mips_pc16: relative address out of range 0x%x\n", +			 rel);  		return -ENOEXEC;  	} @@ -415,8 +287,7 @@ 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"); +		pr_debug("VPE loader: apply_r_mips_26: unaligned relocation\n");  		return -ENOEXEC;  	} @@ -447,7 +318,7 @@ static int apply_r_mips_hi16(struct module *me, uint32_t *location,  	 * the carry we need to add.  Save the information, and let LO16 do the  	 * actual relocation.  	 */ -	n = kmalloc(sizeof *n, GFP_KERNEL); +	n = kmalloc(sizeof(*n), GFP_KERNEL);  	if (!n)  		return -ENOMEM; @@ -466,7 +337,7 @@ static int apply_r_mips_lo16(struct module *me, uint32_t *location,  	Elf32_Addr val, vallo;  	struct mips_hi16 *l, *next; -	/* Sign extend the addend we extract from the lo insn.  */ +	/* Sign extend the addend we extract from the lo insn.	*/  	vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;  	if (mips_hi16_list != NULL) { @@ -478,10 +349,8 @@ static int apply_r_mips_lo16(struct module *me, uint32_t *location,  			/*  			 * The value for the HI16 had best be the same.  			 */ - 			if (v != l->value) { -				printk(KERN_DEBUG "VPE loader: " -				       "apply_r_mips_lo16/hi16: \t" -				       "inconsistent value information\n"); +			if (v != l->value) { +				pr_debug("VPE loader: apply_r_mips_lo16/hi16: inconsistent value information\n");  				goto out_free;  			} @@ -513,7 +382,7 @@ static int apply_r_mips_lo16(struct module *me, uint32_t *location,  	}  	/* -	 * Ok, we're done with the HI16 relocs.  Now deal with the LO16. +	 * Ok, we're done with the HI16 relocs.	 Now deal with the LO16.  	 */  	val = v + vallo;  	insnlo = (insnlo & ~0xffff) | (val & 0xffff); @@ -577,20 +446,19 @@ static int apply_relocations(Elf32_Shdr *sechdrs,  			+ ELF32_R_SYM(r_info);  		if (!sym->st_value) { -			printk(KERN_DEBUG "%s: undefined weak symbol %s\n", -			       me->name, strtab + sym->st_name); +			pr_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 ) { +		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); +			pr_warn("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;  		}  	} @@ -605,10 +473,8 @@ static inline void save_gp_address(unsigned int secbase, unsigned int rel)  }  /* end module-elf32.c */ - -  /* Change all symbols so that sh_value encodes the pointer directly. */ -static void simplify_symbols(Elf_Shdr * sechdrs, +static void simplify_symbols(Elf_Shdr *sechdrs,  			    unsigned int symindex,  			    const char *strtab,  			    const char *secstrings, @@ -649,18 +515,16 @@ static void simplify_symbols(Elf_Shdr * sechdrs,  			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 +			pr_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) { +			if (strncmp(strtab + sym[i].st_name, "_gp", 3) == 0)  				save_gp_address(secbase, sym[i].st_value); -			}  			sym[i].st_value += secbase;  			break; @@ -669,151 +533,21 @@ static void simplify_symbols(Elf_Shdr * sechdrs,  }  #ifdef DEBUG_ELFLOADER -static void dump_elfsymbols(Elf_Shdr * sechdrs, unsigned int symindex, +static 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); +	pr_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); +		pr_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); - -	/* -	 * SMTC/SMVP kernels manage VPE enable independently, -	 * but uniprocessor kernels need to turn it on, even -	 * if that wasn't the pre-dvpe() state. -	 */ -#ifdef CONFIG_SMP -	evpe(vpeflags); -#else -	evpe(EVPE_ENABLE); -#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, +static int find_vpe_symbols(struct vpe *v, Elf_Shdr *sechdrs,  				      unsigned int symindex, const char *strtab,  				      struct module *mod)  { @@ -821,16 +555,14 @@ static int find_vpe_symbols(struct vpe * v, Elf_Shdr * sechdrs,  	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) { +		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) { +		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)) +	if ((v->__start == 0) || (v->shared_ptr == NULL))  		return -1;  	return 0; @@ -841,14 +573,14 @@ static int find_vpe_symbols(struct vpe * v, Elf_Shdr * sechdrs,   * contents of the program (p)buffer performing relocatations/etc, free's it   * when finished.   */ -static int vpe_elfload(struct vpe * v) +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 +	struct module mod; /* so we can re-use the relocations code */  	memset(&mod, 0, sizeof(struct module));  	strcpy(mod.name, "VPE loader"); @@ -862,8 +594,7 @@ static int vpe_elfload(struct vpe * v)  	    || (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"); +		pr_warn("VPE loader: program wrong arch or weird elf version\n");  		return -ENOEXEC;  	} @@ -872,8 +603,7 @@ static int vpe_elfload(struct vpe * v)  		relocate = 1;  	if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) { -		printk(KERN_ERR "VPE loader: program length %u truncated\n", -		       len); +		pr_err("VPE loader: program length %u truncated\n", len);  		return -ENOEXEC;  	} @@ -888,22 +618,24 @@ static int vpe_elfload(struct vpe * v)  	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", +			if ((sechdrs[i].sh_type != SHT_NOBITS) && +			    (len < sechdrs[i].sh_offset + sechdrs[i].sh_size)) { +				pr_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; +			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; +				strtab = (char *)hdr + +					sechdrs[strindex].sh_offset;  			}  		}  		layout_sections(&mod, hdr, sechdrs, secstrings); @@ -930,39 +662,41 @@ static int vpe_elfload(struct vpe * v)  			/* 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); +			pr_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); +		/* 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; +		/* 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; +			/* 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; +			/* 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; +			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); +		} +	} 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) { @@ -976,16 +710,20 @@ static int vpe_elfload(struct vpe * v)  		}  		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; - 			} +			/* 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 symtab's address for when we try +				 * to find the magic symbols +				 */ +				sechdrs[i].sh_addr = (size_t) hdr + +					sechdrs[i].sh_offset; +			}  		}  	} @@ -995,53 +733,19 @@ static int vpe_elfload(struct vpe * v)  	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"); +			pr_warn("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"); +			pr_warn("VPE loader: program does not contain vpe_shared symbol.\n" +				" Unable to use AMVP (AP/SP) facilities.\n");  	} -	printk(" elf loaded\n"); +	pr_info(" 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; @@ -1057,60 +761,53 @@ static int getcwd(char *buff, int size)  	return ret;  } -/* checks VPE is unused and gets ready to load program  */ +/* 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_notifications *notifier;  	struct vpe *v;  	int ret; -	if (minor != iminor(inode)) { +	if (VPE_MODULE_MINOR != iminor(inode)) {  		/* assume only 1 device at the moment. */ -		pr_warning("VPE loader: only vpe1 is supported\n"); +		pr_warn("VPE loader: only vpe1 is supported\n");  		return -ENODEV;  	} -	if ((v = get_vpe(tclimit)) == NULL) { -		pr_warning("VPE loader: unable to get vpe\n"); +	v = get_vpe(aprp_cpu_index()); +	if (v == NULL) { +		pr_warn("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"); +		pr_debug("VPE loader: tc in use dumping regs\n"); -		list_for_each_entry(not, &v->notify, list) { -			not->stop(tclimit); -		} +		list_for_each_entry(notifier, &v->notify, list) +			notifier->stop(aprp_cpu_index());  		release_progmem(v->load_addr); -		cleanup_tc(get_tc(tclimit)); +		cleanup_tc(get_tc(aprp_cpu_index()));  	}  	/* this of-course trashes what was there before... */  	v->pbuffer = vmalloc(P_SIZE); +	if (!v->pbuffer) { +		pr_warn("VPE loader: unable to allocate memory\n"); +		return -ENOMEM; +	}  	v->plen = P_SIZE;  	v->load_addr = NULL;  	v->len = 0; -	v->uid = filp->f_cred->fsuid; -	v->gid = filp->f_cred->fsgid; - -#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); +		pr_warn("VPE loader: open, getcwd returned %d\n", ret);  	v->shared_ptr = NULL;  	v->__start = 0; @@ -1124,20 +821,20 @@ static int vpe_release(struct inode *inode, struct file *filp)  	Elf_Ehdr *hdr;  	int ret = 0; -	v = get_vpe(tclimit); +	v = get_vpe(aprp_cpu_index());  	if (v == NULL)  		return -ENODEV;  	hdr = (Elf_Ehdr *) v->pbuffer;  	if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) == 0) { -		if (vpe_elfload(v) >= 0) { +		if ((vpe_elfload(v) >= 0) && vpe_run) {  			vpe_run(v);  		} else { - 			printk(KERN_WARNING "VPE loader: ELF load failed.\n"); +			pr_warn("VPE loader: ELF load failed.\n");  			ret = -ENOEXEC;  		}  	} else { - 		printk(KERN_WARNING "VPE loader: only elf files are supported\n"); +		pr_warn("VPE loader: only elf files are supported\n");  		ret = -ENOEXEC;  	} @@ -1149,34 +846,28 @@ static int vpe_release(struct inode *inode, struct file *filp)  	if (ret < 0)  		v->shared_ptr = NULL; -	// cleanup any temp buffers -	if (v->pbuffer) -		vfree(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) +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) +	if (iminor(file_inode(file)) != VPE_MODULE_MINOR)  		return -ENODEV; -	v = get_vpe(tclimit); +	v = get_vpe(aprp_cpu_index()); +  	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"); +		pr_warn("VPE loader: elf size too big. Perhaps strip uneeded symbols\n");  		return -ENOMEM;  	} @@ -1188,7 +879,7 @@ static ssize_t vpe_write(struct file *file, const char __user * buffer,  	return ret;  } -static const struct file_operations vpe_fops = { +const struct file_operations vpe_fops = {  	.owner = THIS_MODULE,  	.open = vpe_open,  	.release = vpe_release, @@ -1196,427 +887,40 @@ static const struct file_operations vpe_fops = {  	.llseek = noop_llseek,  }; -/* 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; +	struct vpe *v = get_vpe(index); -	if ((v = get_vpe(index)) == NULL) +	if (v == 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; +	struct vpe *v = get_vpe(index); -	if ((v = get_vpe(index)) == NULL) +	if (v == NULL)  		return -1;  	list_add(¬ify->list, &v->notify);  	return 0;  } -  EXPORT_SYMBOL(vpe_notify);  char *vpe_getcwd(int index)  { -	struct vpe *v; +	struct vpe *v = get_vpe(index); -	if ((v = get_vpe(index)) == NULL) +	if (v == NULL)  		return NULL;  	return v->cwd;  } -  EXPORT_SYMBOL(vpe_getcwd); -#ifdef CONFIG_MIPS_APSP_KSPD -static void kspd_sp_exit( int sp_id) -{ -	cleanup_tc(get_tc(sp_id)); -} -#endif - -static 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, -	dev_set_name(&vpe_device, "vpe1"); -	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; - -	device_del(&vpe_device); -	unregister_chrdev(major, module_name); - -	/* No locking needed here */ -	list_for_each_entry_safe(v, n, &vpecontrol.vpe_list, list) { -		if (v->state != VPE_STATE_UNUSED) -			release_vpe(v); -	} -} -  module_init(vpe_module_init);  module_exit(vpe_module_exit);  MODULE_DESCRIPTION("MIPS VPE Loader");  | 
