--- zzzz-none-000/linux-3.10.107/arch/mips/kernel/vpe.c 2017-06-27 09:49:32.000000000 +0000 +++ vr9-7490-729/linux-3.10.107/arch/mips/kernel/vpe.c 2021-11-10 11:53:54.000000000 +0000 @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +50,7 @@ #include #include #include +#include #include typedef void *vpe_handle; @@ -94,6 +96,16 @@ TC_STATE_DYNAMIC }; +enum load_state { + LOAD_STATE_EHDR, + LOAD_STATE_PHDR, + LOAD_STATE_SHDR, + LOAD_STATE_PIMAGE, + LOAD_STATE_TRAILER, + LOAD_STATE_DONE, + LOAD_STATE_ERROR +}; + struct vpe { enum vpe_state state; @@ -101,10 +113,25 @@ int minor; /* elfloader stuff */ + unsigned long offset; /* File offset into input stream */ void *load_addr; - unsigned long len; + unsigned long copied; char *pbuffer; - unsigned long plen; + unsigned long pbsize; + /* Program loading state */ + enum load_state l_state; + Elf_Ehdr *l_ehdr; + struct elf_phdr *l_phdr; + unsigned int l_phlen; + Elf_Shdr *l_shdr; + unsigned int l_shlen; + int *l_phsort; /* Sorted index list of program headers */ + int l_segoff; /* Offset into current program segment */ + int l_cur_seg; /* Indirect index of segment currently being loaded */ + unsigned int l_progminad; + unsigned int l_progmaxad; + unsigned int l_trailer; + unsigned int uid, gid; char cwd[VPE_PATH_MAX]; @@ -116,9 +143,12 @@ /* The list of vpe's */ struct list_head list; - /* shared symbol address */ + /* legacy shared symbol address */ void *shared_ptr; + /* shared area descriptor array address */ + struct vpe_shared_area *shared_areas; + /* the list of who wants to know when something major happens */ struct list_head notify; @@ -140,13 +170,24 @@ spinlock_t tc_list_lock; struct list_head tc_list; /* Thread contexts */ } vpecontrol = { - .vpe_list_lock = __SPIN_LOCK_UNLOCKED(vpe_list_lock), + .vpe_list_lock = __SPIN_LOCK_UNLOCKED(vpecontrol.vpe_list_lock), .vpe_list = LIST_HEAD_INIT(vpecontrol.vpe_list), - .tc_list_lock = __SPIN_LOCK_UNLOCKED(tc_list_lock), + .tc_list_lock = __SPIN_LOCK_UNLOCKED(vpecontrol.tc_list_lock), .tc_list = LIST_HEAD_INIT(vpecontrol.tc_list) }; static void release_progmem(void *ptr); +/* + * Values and state associated with publishing shared memory areas + */ + +#define N_PUB_AREAS 4 + +static struct vpe_shared_area published_vpe_area[N_PUB_AREAS] = { + {VPE_SHARED_RESERVED, 0}, + {VPE_SHARED_RESERVED, 0}, + {VPE_SHARED_RESERVED, 0}, + {VPE_SHARED_RESERVED, 0} }; /* get the vpe associated with this minor */ static struct vpe *get_vpe(int minor) @@ -169,7 +210,7 @@ return res; } -/* get the vpe associated with this minor */ +/* get the tc associated with this minor */ static struct tc *get_tc(int index) { struct tc *res, *t; @@ -194,7 +235,8 @@ if ((v = kzalloc(sizeof(struct vpe), GFP_KERNEL)) == NULL) return NULL; - + printk(KERN_DEBUG "Used kzalloc to allocate %d bytes at %x\n", + sizeof(struct vpe), (unsigned int)v); INIT_LIST_HEAD(&v->tc); spin_lock(&vpecontrol.vpe_list_lock); list_add_tail(&v->list, &vpecontrol.vpe_list); @@ -213,7 +255,8 @@ if ((tc = kzalloc(sizeof(struct tc), GFP_KERNEL)) == NULL) goto out; - + printk(KERN_DEBUG "Used kzalloc to allocate %d bytes at %x\n", + sizeof(struct tc), (unsigned int)tc); INIT_LIST_HEAD(&tc->tc); tc->index = index; @@ -231,6 +274,11 @@ list_del(&v->list); if (v->load_addr) release_progmem(v); + printk(KERN_DEBUG "Used kfree to free memory at %x\n", + (unsigned int)v->l_phsort); + kfree(v->l_phsort); + printk(KERN_DEBUG "Used kfree to free memory at %x\n", + (unsigned int)v); kfree(v); } @@ -254,8 +302,88 @@ val & MVPCONF0_PTC, (val & MVPCONF0_M) >> MVPCONF0_M_SHIFT); } -/* Find some VPE program space */ -static void *alloc_progmem(unsigned long len) +/* + * The original APRP prototype assumed a single, unshared IRQ for + * cross-VPE interrupts, used by the RTLX code. But M3P networking + * and other future functions may need to share an IRQ, particularly + * in 34K/Malta configurations without an external interrupt controller. + * All cross-VPE insterrupt users need to coordinate through shared + * functions here. + */ + +/* + * It would be nice if I could just have this initialized to zero, + * but the patchcheck police won't hear of it... + */ + +static int xvpe_vector_set; + +#define XVPE_INTR_OFFSET 0 + +static int xvpe_irq = MIPS_CPU_IRQ_BASE + XVPE_INTR_OFFSET; + +static void xvpe_dispatch(void) +{ + do_IRQ(xvpe_irq); +} + +/* Name here is generic, as m3pnet.c could in principle be used by non-MIPS */ +int arch_get_xcpu_irq() +{ + /* + * Some of this will ultimately become platform code, + * but for now, we're only targeting 34K/FPGA/Malta, + * and there's only one generic mechanism. + */ + if (!xvpe_vector_set) { + /* + * A more elaborate shared variable shouldn't be needed. + * Two initializations back-to-back should be harmless. + */ + if (cpu_has_vint) { + set_vi_handler(XVPE_INTR_OFFSET, xvpe_dispatch); + xvpe_vector_set = 1; + } else { + printk(KERN_ERR "APRP requires vectored interrupts\n"); + return -1; + } + } + + return xvpe_irq; +} +EXPORT_SYMBOL(arch_get_xcpu_irq); + +int vpe_send_interrupt(int vpe, int inter) +{ + unsigned long flags; + unsigned int vpeflags; + + local_irq_save(flags); + vpeflags = dvpe(); + + /* + * Initial version makes same simple-minded assumption + * as is implicit elsewhere in this module, that the + * only RP of interest is using the first non-Linux TC. + * We ignore the parameters provided by the caller! + */ + settc(tclimit); + /* + * In 34K/Malta, the only cross-VPE interrupts possible + * are done by setting SWINT bits in Cause, of which there + * are two. SMTC uses SW1 for a multiplexed class of IPIs, + * and this mechanism should be generalized to APRP and use + * the same protocol. Until that's implemented, send only + * SW0 here, regardless of requested type. + */ + write_vpe_c0_cause(read_vpe_c0_cause() | C_SW0); + evpe(vpeflags); + local_irq_restore(flags); + return 1; +} +EXPORT_SYMBOL(vpe_send_interrupt); +/* Find some VPE program space */ +static void *alloc_progmem(void *requested, unsigned long len) { void *addr; @@ -265,10 +393,28 @@ * physically have, for example by passing a mem= boot argument. */ addr = pfn_to_kaddr(max_low_pfn); - memset(addr, 0, len); + if (requested != 0) { + if (requested >= addr) + addr = requested; + else + addr = 0; + } + if (addr != 0) + memset(addr, 0, len); + printk(KERN_DEBUG "pfn_to_kaddr returns %lu bytes of memory at %x\n", + len, (unsigned int)addr); #else - /* simple grab some mem for now */ - addr = kzalloc(len, GFP_KERNEL); + if (requested != 0) { + /* If we have a target in mind, grab a 2x slice and hope... */ + addr = kzalloc(len*2, GFP_KERNEL); + if ((requested >= addr) && (requested < (addr + len))) + addr = requested; + else + addr = 0; + } else { + /* simply grab some mem for now */ + addr = kzalloc(len, GFP_KERNEL); + } #endif return addr; @@ -440,6 +586,8 @@ * actual relocation. */ n = kmalloc(sizeof *n, GFP_KERNEL); + printk(KERN_DEBUG "Used kmalloc to allocate %d bytes at %x\n", + sizeof(struct mips_hi16), (unsigned int)n); if (!n) return -ENOMEM; @@ -497,6 +645,8 @@ *l->addr = insn; next = l->next; + printk(KERN_DEBUG "Used kfree to free memory at %x\n", + (unsigned int)l); kfree(l); l = next; } @@ -590,7 +740,7 @@ return 0; } -static inline void save_gp_address(unsigned int secbase, unsigned int rel) +void save_gp_address(unsigned int secbase, unsigned int rel) { gp_addr = secbase + rel; gp_offs = gp_addr - (secbase & 0xffff0000); @@ -697,7 +847,18 @@ dmt_flag = dmt(); vpeflags = dvpe(); - if (list_empty(&v->tc)) { + 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); @@ -709,8 +870,6 @@ return -ENOEXEC; } - t = list_first_entry(&v->tc, struct tc, tc); - /* Put MVPE's into 'configuration state' */ set_c0_mvpcontrol(MVPCONTROL_VPC); @@ -801,16 +960,41 @@ struct module *mod) { Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr; - unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym); + unsigned int i, j, 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, "__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 (strcmp(strtab + sym[i].st_name, "vpe_shared") == 0) + v->shared_ptr = (void *)sym[i].st_value; + + if (strcmp(strtab + sym[i].st_name, "_vpe_shared_areas") == 0) { + struct vpe_shared_area *psa + = (struct vpe_shared_area *)sym[i].st_value; + struct vpe_shared_area *tpsa; + v->shared_areas = psa; + printk(KERN_INFO"_vpe_shared_areas found, 0x%x\n", + (unsigned int)v->shared_areas); + /* + * Copy any "published" areas to the descriptor + */ + for (j = 0; j < N_PUB_AREAS; j++) { + if (published_vpe_area[j].type != VPE_SHARED_RESERVED) { + tpsa = psa; + while (tpsa->type != VPE_SHARED_NULL) { + if ((tpsa->type == VPE_SHARED_RESERVED) + || (tpsa->type == published_vpe_area[j].type)) { + tpsa->type = published_vpe_area[j].type; + tpsa->addr = published_vpe_area[j].addr; + break; + } + tpsa++; + } + } } + } + } if ( (v->__start == 0) || (v->shared_ptr == NULL)) @@ -832,17 +1016,16 @@ 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 + mm_segment_t old_fs; memset(&mod, 0, sizeof(struct module)); strcpy(mod.name, "VPE loader"); - - hdr = (Elf_Ehdr *) v->pbuffer; - len = v->plen; + hdr = v->l_ehdr; + len = v->pbsize; /* Sanity checks against insmoding binaries or wrong arch, weird elf version */ - if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0 - || (hdr->e_type != ET_REL && hdr->e_type != ET_EXEC) + if ((hdr->e_type != ET_REL && hdr->e_type != ET_EXEC) || !elf_check_arch(hdr) || hdr->e_shentsize != sizeof(*sechdrs)) { printk(KERN_WARNING @@ -854,9 +1037,8 @@ 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); + if (len < v->l_phlen + v->l_shlen) { + printk(KERN_ERR "VPE loader: Headers exceed %u bytes\n", len); return -ENOEXEC; } @@ -890,9 +1072,13 @@ } } layout_sections(&mod, hdr, sechdrs, secstrings); + /* + * Non-relocatable loads should have already done their + * allocates, based on program header table. + */ } - v->load_addr = alloc_progmem(mod.core_size); + memset(v->load_addr, 0, mod.core_size); if (!v->load_addr) return -ENOMEM; @@ -958,6 +1144,9 @@ phdr++; } + /* + * Program image is already in memory. + */ for (i = 0; i < hdr->e_shnum; i++) { /* Internal symbols and strings. */ if (sechdrs[i].sh_type == SHT_SYMTAB) { @@ -973,8 +1162,12 @@ } /* make sure it's physically written out */ + /* flush the icache in correct context */ + old_fs = get_fs(); + set_fs(KERNEL_DS); flush_icache_range((unsigned long)v->load_addr, - (unsigned long)v->load_addr + v->len); + (unsigned long)v->load_addr + v->copied); + set_fs(old_fs); if ((find_vpe_symbols(v, sechdrs, symindex, strtab, &mod)) < 0) { if (v->__start == 0) { @@ -988,8 +1181,8 @@ "program does not contain vpe_shared symbol.\n" " Unable to use AMVP (AP/SP) facilities.\n"); } + pr_info("APRP VPE loader: elf loaded\n"); - printk(" elf loaded\n"); return 0; } @@ -1054,6 +1247,10 @@ return -ENODEV; } + /* + * This treats the tclimit command line configuration input + * as a minor device indication, which is probably unwholesome. + */ if ((v = get_vpe(tclimit)) == NULL) { pr_warning("VPE loader: unable to get vpe\n"); @@ -1070,18 +1267,20 @@ } release_progmem(v->load_addr); + kfree(v->l_phsort); cleanup_tc(get_tc(tclimit)); } /* this of-course trashes what was there before... */ v->pbuffer = vmalloc(P_SIZE); - if (!v->pbuffer) { - pr_warning("VPE loader: unable to allocate memory\n"); - return -ENOMEM; - } - v->plen = P_SIZE; v->load_addr = NULL; - v->len = 0; + v->copied = 0; + v->offset = 0; + v->l_state = LOAD_STATE_EHDR; + v->l_ehdr = NULL; + v->l_phdr = NULL; + v->l_phsort = NULL; + v->l_shdr = NULL; v->uid = filp->f_cred->fsuid; v->gid = filp->f_cred->fsgid; @@ -1092,6 +1291,7 @@ printk(KERN_WARNING "VPE loader: open, getcwd returned %d\n", ret); v->shared_ptr = NULL; + v->shared_areas = NULL; v->__start = 0; return 0; @@ -1100,18 +1300,30 @@ static int vpe_release(struct inode *inode, struct file *filp) { struct vpe *v; - Elf_Ehdr *hdr; int ret = 0; v = get_vpe(tclimit); if (v == NULL) return -ENODEV; + /* + * If image load had no errors, massage program/section tables + * to reflect movement of program/section data into VPE program + * memory. + */ + if (v->l_state != LOAD_STATE_DONE) { + printk(KERN_WARNING "VPE Release after incomplete load\n"); + printk(KERN_DEBUG "Used vfree to free memory at " + "%x after failed load attempt\n", + (unsigned int)v->pbuffer); + if (v->pbuffer != NULL) + vfree(v->pbuffer); + return -ENOEXEC; + } - hdr = (Elf_Ehdr *) v->pbuffer; - if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) == 0) { - if (vpe_elfload(v) >= 0) { + if (memcmp(((Elf_Ehdr *)v->pbuffer)->e_ident, ELFMAG, SELFMAG) == 0) { + if (vpe_elfload(v) >= 0) vpe_run(v); - } else { + else { printk(KERN_WARNING "VPE loader: ELF load failed.\n"); ret = -ENOEXEC; } @@ -1120,6 +1332,7 @@ ret = -ENOEXEC; } + /* It's good to be able to run the SP and if it chokes have a look at the /dev/rt?. But if we reset the pointer to the shared struct we lose what has happened. So perhaps if garbage is sent to the vpe @@ -1128,36 +1341,427 @@ if (ret < 0) v->shared_ptr = NULL; - vfree(v->pbuffer); - v->plen = 0; - + // cleanup any temp buffers + if (v->pbuffer) { + printk(KERN_DEBUG "Used vfree to free memory at %x\n", + (unsigned int)v->pbuffer); + vfree(v->pbuffer); + } + v->pbsize = 0; return ret; } +/* + * A sort of insertion sort to generate list of program header indices + * in order of their file offsets. + */ + +static void indexort(struct elf_phdr *phdr, int nph, int *index) +{ + int i, j, t; + unsigned int toff; + + /* Create initial mapping */ + for (i = 0; i < nph; i++) + index[i] = i; + /* Do the indexed insert sort */ + for (i = 1; i < nph; i++) { + j = i; + t = index[j]; + toff = phdr[t].p_offset; + while ((j > 0) && (phdr[index[j-1]].p_offset > toff)) { + index[j] = index[j-1]; + j--; + } + index[j] = t; + } +} + + +/* + * This function has to convert the ELF file image being sequentially + * streamed to the pseudo-device into the binary image, symbol, and + * string information, which the ELF format allows to be in some degree + * of disorder. + * + * The ELF header and, if present, program header table, are copied into + * a temporary buffer. Loadable program segments, if present, are copied + * into the RP program memory at the addresses specified by the program + * header table. + * + * Sections not specified by the program header table are loaded into + * memory following the program segments if they are "allocated", or + * into the temporary buffer if they are not. The section header + * table is loaded into the temporary buffer.??? + */ +#define CURPHDR (v->l_phdr[v->l_phsort[v->l_cur_seg]]) 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; + int tocopy, uncopied; + int i; + unsigned int progmemlen; - if (iminor(file_inode(file)) != minor) + if (iminor(file->f_path.dentry->d_inode) != minor) return -ENODEV; v = get_vpe(tclimit); if (v == NULL) return -ENODEV; - if ((count + v->len) > v->plen) { - printk(KERN_WARNING - "VPE loader: elf size too big. Perhaps strip uneeded symbols\n"); + if (v->pbuffer == NULL) { + printk(KERN_ERR "VPE loader: no buffer for program\n"); return -ENOMEM; } - count -= copy_from_user(v->pbuffer + v->len, buffer, count); - if (!count) - return -EFAULT; + while (count) { + switch (v->l_state) { + case LOAD_STATE_EHDR: + /* Loading ELF Header into scratch buffer */ + tocopy = min((unsigned long)count, + sizeof(Elf_Ehdr) - v->offset); + uncopied = copy_from_user(v->pbuffer + v->copied, + buffer, tocopy); + count -= tocopy - uncopied; + v->copied += tocopy - uncopied; + v->offset += tocopy - uncopied; + buffer += tocopy - uncopied; + if (v->copied == sizeof(Elf_Ehdr)) { + v->l_ehdr = (Elf_Ehdr *)v->pbuffer; + if (memcmp(v->l_ehdr->e_ident, ELFMAG, 4) != 0) { + printk(KERN_WARNING "VPE loader: %s\n", + "non-ELF file image"); + ret = -ENOEXEC; + v->l_state = LOAD_STATE_ERROR; + break; + } + if (v->l_ehdr->e_phoff != 0) { + v->l_phdr = (struct elf_phdr *) + (v->pbuffer + v->l_ehdr->e_phoff); + v->l_phlen = v->l_ehdr->e_phentsize + * v->l_ehdr->e_phnum; + /* Check against buffer overflow */ + if ((v->copied + v->l_phlen) > v->pbsize) { + printk(KERN_WARNING + "VPE loader: elf program header table size too big\n"); + v->l_state = LOAD_STATE_ERROR; + return -ENOMEM; + } + v->l_state = LOAD_STATE_PHDR; + /* + * Program headers generally indicate + * linked executable with possibly + * valid entry point. + */ + v->__start = v->l_ehdr->e_entry; + } else if (v->l_ehdr->e_shoff != 0) { + /* + * No program headers, but a section + * header table. A relocatable binary. + * We need to load the works into the + * kernel temp buffer to compute the + * RP program image. That limits our + * binary size, but at least we're no + * worse off than the original APRP + * prototype. + */ + v->l_shlen = v->l_ehdr->e_shentsize + * v->l_ehdr->e_shnum; + if ((v->l_ehdr->e_shoff + v->l_shlen + - v->offset) > v->pbsize) { + printk(KERN_WARNING + "VPE loader: elf sections/section table too big.\n"); + v->l_state = LOAD_STATE_ERROR; + return -ENOMEM; + } + v->l_state = LOAD_STATE_SHDR; + } else { + /* + * If neither program nor section tables, + * we don't know what to do. + */ + v->l_state = LOAD_STATE_ERROR; + return -ENOEXEC; + } + } + break; + case LOAD_STATE_PHDR: + /* Loading Program Headers into scratch */ + tocopy = min((unsigned long)count, + v->l_ehdr->e_phoff + v->l_phlen - v->copied); + uncopied = copy_from_user(v->pbuffer + v->copied, + buffer, tocopy); + count -= tocopy - uncopied; + v->copied += tocopy - uncopied; + v->offset += tocopy - uncopied; + buffer += tocopy - uncopied; + + if (v->copied == v->l_ehdr->e_phoff + v->l_phlen) { + /* + * It's legal for the program headers to be + * out of order with respect to the file layout. + * Generate a list of indices, sorted by file + * offset. + */ + v->l_phsort = kmalloc(v->l_ehdr->e_phnum + * sizeof(int), GFP_KERNEL); + printk(KERN_DEBUG + "Used kmalloc to allocate %d bytes of memory at %x\n", + v->l_ehdr->e_phnum*sizeof(int), + (unsigned int)v->l_phsort); + if (!v->l_phsort) + return -ENOMEM; /* Preposterous, but... */ + indexort(v->l_phdr, v->l_ehdr->e_phnum, + v->l_phsort); + + v->l_progminad = (unsigned int)-1; + v->l_progmaxad = 0; + progmemlen = 0; + for (i = 0; i < v->l_ehdr->e_phnum; i++) { + if (v->l_phdr[v->l_phsort[i]].p_type + == PT_LOAD) { + /* Unstripped .reginfo sections are bad */ + if (v->l_phdr[v->l_phsort[i]].p_vaddr + < __UA_LIMIT) { + printk(KERN_WARNING "%s%s%s\n", + "VPE loader: ", + "User-mode p_vaddr, ", + "skipping program segment,"); + printk(KERN_WARNING "%s%s%s\n", + "VPE loader: ", + "strip .reginfo from binary ", + "if necessary."); + continue; + } + if (v->l_phdr[v->l_phsort[i]].p_vaddr + < v->l_progminad) + v->l_progminad = + v->l_phdr[v->l_phsort[i]].p_vaddr; + if ((v->l_phdr[v->l_phsort[i]].p_vaddr + + v->l_phdr[v->l_phsort[i]].p_memsz) + > v->l_progmaxad) + v->l_progmaxad = + v->l_phdr[v->l_phsort[i]].p_vaddr + + v->l_phdr[v->l_phsort[i]].p_memsz; + } + } + printk(KERN_INFO "APRP RP program 0x%x to 0x%x\n", + v->l_progminad, v->l_progmaxad); + /* + * Do a simple sanity check of the memory being + * allocated. Abort if greater than an arbitrary + * value of 32MB + */ + if (v->l_progmaxad - v->l_progminad > + 32*1024*1024) { + printk(KERN_WARNING + "RP program failed to allocate %d kbytes - limit is 32,768 KB\n", + (v->l_progmaxad - v->l_progminad)/1024); + return -ENOMEM; + } + + v->load_addr = alloc_progmem((void *)v->l_progminad, + v->l_progmaxad - v->l_progminad); + if (!v->load_addr) + return -ENOMEM; + if ((unsigned int)v->load_addr + > v->l_progminad) { + release_progmem(v->load_addr); + return -ENOMEM; + } + /* Find first segment with loadable content */ + for (i = 0; i < v->l_ehdr->e_phnum; i++) { + if (v->l_phdr[v->l_phsort[i]].p_type + == PT_LOAD) { + if (v->l_phdr[v->l_phsort[i]].p_vaddr + < __UA_LIMIT) { + /* Skip userspace segments */ + continue; + } + v->l_cur_seg = i; + break; + } + } + if (i == v->l_ehdr->e_phnum) { + /* No loadable program segment? Bogus file. */ + printk(KERN_WARNING "Bad ELF file for APRP\n"); + return -ENOEXEC; + } + v->l_segoff = 0; + v->l_state = LOAD_STATE_PIMAGE; + } + break; + case LOAD_STATE_PIMAGE: + /* + * Skip through input stream until + * first program segment. Would be + * better to have loaded up to here + * into the temp buffer, but for now + * we simply rule out "interesting" + * sections prior to the last program + * segment in an executable file. + */ + if (v->offset < CURPHDR.p_offset) { + uncopied = CURPHDR.p_offset - v->offset; + if (uncopied > count) + uncopied = count; + count -= uncopied; + buffer += uncopied; + v->offset += uncopied; + /* Go back through the "while" */ + break; + } + /* + * Having dispensed with any unlikely fluff, + * copy from user I/O buffer to program segment. + */ + tocopy = min(count, CURPHDR.p_filesz - v->l_segoff); + + /* Loading image into RP memory */ + uncopied = copy_from_user((char *)CURPHDR.p_vaddr + + v->l_segoff, buffer, tocopy); + count -= tocopy - uncopied; + v->offset += tocopy - uncopied; + v->l_segoff += tocopy - uncopied; + buffer += tocopy - uncopied; + if (v->l_segoff >= CURPHDR.p_filesz) { + /* Finished current segment load */ + /* Zero out non-file-sourced image */ + uncopied = CURPHDR.p_memsz - CURPHDR.p_filesz; + if (uncopied > 0) + memset((char *)CURPHDR.p_vaddr + v->l_segoff, + 0, uncopied); + /* Advance to next segment */ + for (i = v->l_cur_seg + 1; + i < v->l_ehdr->e_phnum; i++) { + if (v->l_phdr[v->l_phsort[i]].p_type + == PT_LOAD) { + if (v->l_phdr[v->l_phsort[i]].p_vaddr + < __UA_LIMIT) { + /* Skip userspace segments */ + continue; + } + v->l_cur_seg = i; + break; + } + } + /* If none left, prepare to load section headers */ + if (i == v->l_ehdr->e_phnum) { + if (v->l_ehdr->e_shoff != 0) { + /* Copy to where we left off in temp buffer */ + v->l_shlen = v->l_ehdr->e_shentsize + * v->l_ehdr->e_shnum; + if ((v->l_ehdr->e_shoff + v->l_shlen + - v->offset) > v->pbsize) { + printk(KERN_WARNING + "VPE loader: elf sections/section table too big\n"); + v->l_state = LOAD_STATE_ERROR; + return -ENOMEM; + } + v->l_state = LOAD_STATE_SHDR; + break; + } + } else { + /* reset offset for new program segment */ + v->l_segoff = 0; + } + } + break; + case LOAD_STATE_SHDR: + /* + * Read stream into private buffer up + * through and including the section header + * table. + */ - v->len += count; + tocopy = min((unsigned long)count, + v->l_ehdr->e_shoff + v->l_shlen - v->offset); + if (tocopy) { + uncopied = copy_from_user(v->pbuffer + v->copied, + buffer, tocopy); + count -= tocopy - uncopied; + v->copied += tocopy - uncopied; + v->offset += tocopy - uncopied; + buffer += tocopy - uncopied; + } + /* Finished? */ + if (v->offset == v->l_ehdr->e_shoff + v->l_shlen) { + unsigned int offset_delta = v->offset - v->copied; + + v->l_shdr = (Elf_Shdr *)(v->pbuffer + + v->l_ehdr->e_shoff - offset_delta); + /* + * Check for sections after the section table, + * which for gcc MIPS binaries includes + * the symbol table. Do any other processing + * that requires value within stream, and + * normalize offsets to be relative to + * the header-only layout of temp buffer. + */ + + /* Assume no trailer until we detect one */ + v->l_trailer = 0; + v->l_state = LOAD_STATE_DONE; + for (i = 0; i < v->l_ehdr->e_shnum; i++) { + if (v->l_shdr[i].sh_offset + > v->l_ehdr->e_shoff) { + v->l_state = LOAD_STATE_TRAILER; + /* Track trailing data length */ + if (v->l_trailer + < (v->l_shdr[i].sh_offset + + v->l_shdr[i].sh_size) + - (v->l_ehdr->e_shoff + + v->l_shlen)) + v->l_trailer = + (v->l_shdr[i].sh_offset + + v->l_shdr[i].sh_size) + - (v->l_ehdr->e_shoff + + v->l_shlen); + } + /* Adjust section offset if necessary */ + v->l_shdr[i].sh_offset -= offset_delta; + } + if ((v->copied + v->l_trailer) > v->pbsize) { + printk(KERN_WARNING + "VPE loader: elf size too big. Perhaps strip uneeded symbols\n"); + v->l_state = LOAD_STATE_ERROR; + return -ENOMEM; + } + + /* Fix up offsets in ELF header */ + v->l_ehdr->e_shoff = (unsigned int)v->l_shdr + - (unsigned int)v->pbuffer; + } + break; + case LOAD_STATE_TRAILER: + /* + * Symbol and string tables follow section headers + * in gcc binaries for MIPS. Copy into temp buffer. + */ + if (v->l_trailer) { + tocopy = min(count, v->l_trailer); + uncopied = copy_from_user(v->pbuffer + v->copied, + buffer, tocopy); + count -= tocopy - uncopied; + v->l_trailer -= tocopy - uncopied; + v->copied += tocopy - uncopied; + v->offset += tocopy - uncopied; + buffer += tocopy - uncopied; + } + if (!v->l_trailer) + v->l_state = LOAD_STATE_DONE; + break; + case LOAD_STATE_DONE: + if (count) + count = 0; + break; + case LOAD_STATE_ERROR: + default: + return -EINVAL; + } + } return ret; } @@ -1193,7 +1797,9 @@ { struct vpe *v = vpe; - v->__start = start; + /* Null start address means use value from ELF file */ + if (start) + v->__start = start; return vpe_run(v); } @@ -1318,6 +1924,106 @@ EXPORT_SYMBOL(vpe_getcwd); +/* + * RP applications may contain a _vpe_shared_area descriptor + * array to allow for data sharing with Linux kernel functions + * that's slightly more abstracted and extensible than the + * fixed binding used by the rtlx support. Indeed, the rtlx + * support should ideally be converted to use the generic + * shared area descriptor scheme at some point. + * + * mips_get_vpe_shared_area() can be used by AP kernel + * modules to get an area pointer of a given type, if + * it exists. + * + * mips_publish_vpe_area() is used by AP kernel modules + * to share kseg0 kernel memory with the RP. It maintains + * a private table, so that publishing can be done before + * the RP program is launched. Making this table dynamically + * allocated and extensible would be good scalable OS design. + * however, until there's more than one user of the mechanism, + * it should be an acceptable simplification to allow a static + * maximum of 4 published areas. + */ + +void *mips_get_vpe_shared_area(int index, int type) +{ + struct vpe *v; + struct vpe_shared_area *vsa; + + v = get_vpe(index); + if (v == NULL) + return NULL; + + if (v->shared_areas == NULL) + return NULL; + + vsa = v->shared_areas; + + while (vsa->type != VPE_SHARED_NULL) { + if (vsa->type == type) + return vsa->addr; + else + vsa++; + } + /* Fell through without finding type */ + + return NULL; +} +EXPORT_SYMBOL(mips_get_vpe_shared_area); + +int mips_publish_vpe_area(int type, void *ptr) +{ + int i; + int retval = 0; + struct vpe *v; + unsigned long flags; + unsigned int vpflags; + + printk(KERN_INFO "mips_publish_vpe_area(0x%x, 0x%x)\n", type, (int)ptr); + if ((unsigned int)ptr >= KSEG2) { + printk(KERN_ERR "VPE area pubish of invalid address 0x%x\n", + (int)ptr); + return 0; + } + for (i = 0; i < N_PUB_AREAS; i++) { + if (published_vpe_area[i].type == VPE_SHARED_RESERVED) { + published_vpe_area[i].type = type; + published_vpe_area[i].addr = ptr; + retval = type; + break; + } + } + /* + * If we've already got a VPE up and running, try to + * update the shared descriptor with the new data. + */ + list_for_each_entry(v, &vpecontrol.vpe_list, list) { + if (v->shared_areas != NULL) { + local_irq_save(flags); + vpflags = dvpe(); + for (i = 0; v->shared_areas[i].type != VPE_SHARED_NULL; i++) { + if ((v->shared_areas[i].type == type) + || (v->shared_areas[i].type == VPE_SHARED_RESERVED)) { + v->shared_areas[i].type = type; + v->shared_areas[i].addr = ptr; + } + } + evpe(vpflags); + local_irq_restore(flags); + } + } + return retval; +} +EXPORT_SYMBOL(mips_publish_vpe_area); + +#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) { @@ -1329,6 +2035,7 @@ } release_progmem(vpe->load_addr); + kfree(vpe->l_phsort); cleanup_tc(get_tc(tclimit)); vpe_stop(vpe); vpe_free(vpe); @@ -1374,6 +2081,8 @@ static void vpe_device_release(struct device *cd) { + printk(KERN_DEBUG "Using kfree to free vpe class device at %x\n", + (unsigned int)cd); kfree(cd); } @@ -1400,19 +2109,24 @@ } if (vpelimit == 0) { - printk(KERN_WARNING "No VPEs reserved for AP/SP, not " - "initializing VPE loader.\nPass maxvpes= argument as " - "kernel argument\n"); - +#if defined(CONFIG_MIPS_MT_SMTC) || defined(MIPS_MT_SMP) + printk(KERN_WARNING "No VPEs reserved for VPE loader.\n" + "Pass maxvpes= argument as kernel argument\n"); return -ENODEV; +#else + vpelimit = 1; +#endif } if (tclimit == 0) { +#if defined(CONFIG_MIPS_MT_SMTC) || defined(MIPS_MT_SMP) printk(KERN_WARNING "No TCs reserved for AP/SP, not " "initializing VPE loader.\nPass maxtcs= argument as " "kernel argument\n"); - return -ENODEV; +#else + tclimit = 1; +#endif } major = register_chrdev(0, module_name, &vpe_fops); @@ -1426,7 +2140,7 @@ printk(KERN_ERR "vpe_class registration failed\n"); goto out_chrdev; } - + xvpe_vector_set = 0; device_initialize(&vpe_device); vpe_device.class = &vpe_class, vpe_device.parent = NULL,