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