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