at v3.5 159 lines 4.1 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/elf.h> 21#include <asm/pgtable.h> 22#include <asm/pgalloc.h> 23#include <asm/sections.h> 24#include <arch/sim_def.h> 25 26/* Notify a running simulator, if any, that an exec just occurred. */ 27static void sim_notify_exec(const char *binary_name) 28{ 29 unsigned char c; 30 do { 31 c = *binary_name++; 32 __insn_mtspr(SPR_SIM_CONTROL, 33 (SIM_CONTROL_OS_EXEC 34 | (c << _SIM_CONTROL_OPERATOR_BITS))); 35 36 } while (c); 37} 38 39static int notify_exec(void) 40{ 41 int retval = 0; /* failure */ 42 struct vm_area_struct *vma = current->mm->mmap; 43 while (vma) { 44 if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) 45 break; 46 vma = vma->vm_next; 47 } 48 if (vma) { 49 char *buf = (char *) __get_free_page(GFP_KERNEL); 50 if (buf) { 51 char *path = d_path(&vma->vm_file->f_path, 52 buf, PAGE_SIZE); 53 if (!IS_ERR(path)) { 54 sim_notify_exec(path); 55 retval = 1; 56 } 57 free_page((unsigned long)buf); 58 } 59 } 60 return retval; 61} 62 63/* Notify a running simulator, if any, that we loaded an interpreter. */ 64static void sim_notify_interp(unsigned long load_addr) 65{ 66 size_t i; 67 for (i = 0; i < sizeof(load_addr); i++) { 68 unsigned char c = load_addr >> (i * 8); 69 __insn_mtspr(SPR_SIM_CONTROL, 70 (SIM_CONTROL_OS_INTERP 71 | (c << _SIM_CONTROL_OPERATOR_BITS))); 72 } 73} 74 75 76/* Kernel address of page used to map read-only kernel data into userspace. */ 77static void *vdso_page; 78 79/* One-entry array used for install_special_mapping. */ 80static struct page *vdso_pages[1]; 81 82static int __init vdso_setup(void) 83{ 84 vdso_page = (void *)get_zeroed_page(GFP_ATOMIC); 85 memcpy(vdso_page, __rt_sigreturn, __rt_sigreturn_end - __rt_sigreturn); 86 vdso_pages[0] = virt_to_page(vdso_page); 87 return 0; 88} 89device_initcall(vdso_setup); 90 91const char *arch_vma_name(struct vm_area_struct *vma) 92{ 93 if (vma->vm_private_data == vdso_pages) 94 return "[vdso]"; 95#ifndef __tilegx__ 96 if (vma->vm_start == MEM_USER_INTRPT) 97 return "[intrpt]"; 98#endif 99 return NULL; 100} 101 102int arch_setup_additional_pages(struct linux_binprm *bprm, 103 int executable_stack) 104{ 105 struct mm_struct *mm = current->mm; 106 unsigned long vdso_base; 107 int retval = 0; 108 109 /* 110 * Notify the simulator that an exec just occurred. 111 * If we can't find the filename of the mapping, just use 112 * whatever was passed as the linux_binprm filename. 113 */ 114 if (!notify_exec()) 115 sim_notify_exec(bprm->filename); 116 117 down_write(&mm->mmap_sem); 118 119 /* 120 * MAYWRITE to allow gdb to COW and set breakpoints 121 */ 122 vdso_base = VDSO_BASE; 123 retval = install_special_mapping(mm, vdso_base, PAGE_SIZE, 124 VM_READ|VM_EXEC| 125 VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, 126 vdso_pages); 127 128#ifndef __tilegx__ 129 /* 130 * Set up a user-interrupt mapping here; the user can't 131 * create one themselves since it is above TASK_SIZE. 132 * We make it unwritable by default, so the model for adding 133 * interrupt vectors always involves an mprotect. 134 */ 135 if (!retval) { 136 unsigned long addr = MEM_USER_INTRPT; 137 addr = mmap_region(NULL, addr, INTRPT_SIZE, 138 MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, 139 VM_READ|VM_EXEC| 140 VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, 0); 141 if (addr > (unsigned long) -PAGE_SIZE) 142 retval = (int) addr; 143 } 144#endif 145 146 up_write(&mm->mmap_sem); 147 148 return retval; 149} 150 151 152void elf_plat_init(struct pt_regs *regs, unsigned long load_addr) 153{ 154 /* Zero all registers. */ 155 memset(regs, 0, sizeof(*regs)); 156 157 /* Report the interpreter's load address. */ 158 sim_notify_interp(load_addr); 159}