📄 vpe.c
字号:
/* * 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 + -