at v4.8 165 lines 4.0 kB view raw
1/* 2 * Copyright 2010 Tilera Corporation. All Rights Reserved. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation, version 2. 7 * 8 * This program is distributed in the hope that it will be useful, but 9 * WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 11 * NON INFRINGEMENT. See the GNU General Public License for 12 * more details. 13 */ 14 15#include <linux/mm.h> 16#include <linux/pagemap.h> 17#include <linux/binfmts.h> 18#include <linux/compat.h> 19#include <linux/mman.h> 20#include <linux/file.h> 21#include <linux/elf.h> 22#include <asm/pgtable.h> 23#include <asm/pgalloc.h> 24#include <asm/sections.h> 25#include <asm/vdso.h> 26#include <arch/sim.h> 27 28/* Notify a running simulator, if any, that an exec just occurred. */ 29static void sim_notify_exec(const char *binary_name) 30{ 31 unsigned char c; 32 do { 33 c = *binary_name++; 34 __insn_mtspr(SPR_SIM_CONTROL, 35 (SIM_CONTROL_OS_EXEC 36 | (c << _SIM_CONTROL_OPERATOR_BITS))); 37 38 } while (c); 39} 40 41static int notify_exec(struct mm_struct *mm) 42{ 43 int ret = 0; 44 char *buf, *path; 45 struct vm_area_struct *vma; 46 struct file *exe_file; 47 48 if (!sim_is_simulator()) 49 return 1; 50 51 buf = (char *) __get_free_page(GFP_KERNEL); 52 if (buf == NULL) 53 return 0; 54 55 exe_file = get_mm_exe_file(mm); 56 if (exe_file == NULL) 57 goto done_free; 58 59 path = file_path(exe_file, buf, PAGE_SIZE); 60 if (IS_ERR(path)) 61 goto done_put; 62 63 down_read(&mm->mmap_sem); 64 for (vma = current->mm->mmap; ; vma = vma->vm_next) { 65 if (vma == NULL) { 66 up_read(&mm->mmap_sem); 67 goto done_put; 68 } 69 if (vma->vm_file == exe_file) 70 break; 71 } 72 73 /* 74 * Notify simulator of an ET_DYN object so we know the load address. 75 * The somewhat cryptic overuse of SIM_CONTROL_DLOPEN allows us 76 * to be backward-compatible with older simulator releases. 77 */ 78 if (vma->vm_start == (ELF_ET_DYN_BASE & PAGE_MASK)) { 79 char buf[64]; 80 int i; 81 82 snprintf(buf, sizeof(buf), "0x%lx:@", vma->vm_start); 83 for (i = 0; ; ++i) { 84 char c = buf[i]; 85 __insn_mtspr(SPR_SIM_CONTROL, 86 (SIM_CONTROL_DLOPEN 87 | (c << _SIM_CONTROL_OPERATOR_BITS))); 88 if (c == '\0') { 89 ret = 1; /* success */ 90 break; 91 } 92 } 93 } 94 up_read(&mm->mmap_sem); 95 96 sim_notify_exec(path); 97done_put: 98 fput(exe_file); 99done_free: 100 free_page((unsigned long)buf); 101 return ret; 102} 103 104/* Notify a running simulator, if any, that we loaded an interpreter. */ 105static void sim_notify_interp(unsigned long load_addr) 106{ 107 size_t i; 108 for (i = 0; i < sizeof(load_addr); i++) { 109 unsigned char c = load_addr >> (i * 8); 110 __insn_mtspr(SPR_SIM_CONTROL, 111 (SIM_CONTROL_OS_INTERP 112 | (c << _SIM_CONTROL_OPERATOR_BITS))); 113 } 114} 115 116 117int arch_setup_additional_pages(struct linux_binprm *bprm, 118 int executable_stack) 119{ 120 struct mm_struct *mm = current->mm; 121 int retval = 0; 122 123 /* 124 * Notify the simulator that an exec just occurred. 125 * If we can't find the filename of the mapping, just use 126 * whatever was passed as the linux_binprm filename. 127 */ 128 if (!notify_exec(mm)) 129 sim_notify_exec(bprm->filename); 130 131 down_write(&mm->mmap_sem); 132 133 retval = setup_vdso_pages(); 134 135#ifndef __tilegx__ 136 /* 137 * Set up a user-interrupt mapping here; the user can't 138 * create one themselves since it is above TASK_SIZE. 139 * We make it unwritable by default, so the model for adding 140 * interrupt vectors always involves an mprotect. 141 */ 142 if (!retval) { 143 unsigned long addr = MEM_USER_INTRPT; 144 addr = mmap_region(NULL, addr, INTRPT_SIZE, 145 VM_READ|VM_EXEC| 146 VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, 0); 147 if (addr > (unsigned long) -PAGE_SIZE) 148 retval = (int) addr; 149 } 150#endif 151 152 up_write(&mm->mmap_sem); 153 154 return retval; 155} 156 157 158void elf_plat_init(struct pt_regs *regs, unsigned long load_addr) 159{ 160 /* Zero all registers. */ 161 memset(regs, 0, sizeof(*regs)); 162 163 /* Report the interpreter's load address. */ 164 sim_notify_interp(load_addr); 165}