diff options
Diffstat (limited to 'driver/gator_cookies.c')
-rw-r--r-- | driver/gator_cookies.c | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/driver/gator_cookies.c b/driver/gator_cookies.c new file mode 100644 index 0000000..64be841 --- /dev/null +++ b/driver/gator_cookies.c | |||
@@ -0,0 +1,384 @@ | |||
1 | /** | ||
2 | * Copyright (C) ARM Limited 2010-2011. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #define COOKIEMAP_ENTRIES 1024 /* must be power of 2 */ | ||
11 | #define TRANSLATE_SIZE 256 | ||
12 | #define MAX_COLLISIONS 2 | ||
13 | |||
14 | static uint32_t *gator_crc32_table; | ||
15 | static uint32_t translate_buffer_mask; | ||
16 | |||
17 | static DEFINE_PER_CPU(char *, translate_text); | ||
18 | static DEFINE_PER_CPU(uint32_t, cookie_next_key); | ||
19 | static DEFINE_PER_CPU(uint64_t *, cookie_keys); | ||
20 | static DEFINE_PER_CPU(uint32_t *, cookie_values); | ||
21 | static DEFINE_PER_CPU(int, translate_buffer_read); | ||
22 | static DEFINE_PER_CPU(int, translate_buffer_write); | ||
23 | static DEFINE_PER_CPU(unsigned int *, translate_buffer); | ||
24 | |||
25 | static inline uint32_t get_cookie(int cpu, struct task_struct *task, struct vm_area_struct *vma, struct module *mod); | ||
26 | static void wq_cookie_handler(struct work_struct *unused); | ||
27 | DECLARE_WORK(cookie_work, wq_cookie_handler); | ||
28 | |||
29 | static uint32_t cookiemap_code(uint64_t value64) { | ||
30 | uint32_t value = (uint32_t)((value64 >> 32) + value64); | ||
31 | uint32_t cookiecode = (value >> 24) & 0xff; | ||
32 | cookiecode = cookiecode * 31 + ((value >> 16) & 0xff); | ||
33 | cookiecode = cookiecode * 31 + ((value >> 8) & 0xff); | ||
34 | cookiecode = cookiecode * 31 + ((value >> 0) & 0xff); | ||
35 | cookiecode &= (COOKIEMAP_ENTRIES-1); | ||
36 | return cookiecode * MAX_COLLISIONS; | ||
37 | } | ||
38 | |||
39 | static uint32_t gator_chksum_crc32(char *data) | ||
40 | { | ||
41 | register unsigned long crc; | ||
42 | unsigned char *block = data; | ||
43 | int i, length = strlen(data); | ||
44 | |||
45 | crc = 0xFFFFFFFF; | ||
46 | for (i = 0; i < length; i++) { | ||
47 | crc = ((crc >> 8) & 0x00FFFFFF) ^ gator_crc32_table[(crc ^ *block++) & 0xFF]; | ||
48 | } | ||
49 | |||
50 | return (crc ^ 0xFFFFFFFF); | ||
51 | } | ||
52 | |||
53 | /* | ||
54 | * Exists | ||
55 | * Pre: [0][1][v][3]..[n-1] | ||
56 | * Post: [v][0][1][3]..[n-1] | ||
57 | */ | ||
58 | static uint32_t cookiemap_exists(uint64_t key) { | ||
59 | unsigned long x, flags, retval = 0; | ||
60 | int cpu = smp_processor_id(); | ||
61 | uint32_t cookiecode = cookiemap_code(key); | ||
62 | uint64_t *keys = &(per_cpu(cookie_keys, cpu)[cookiecode]); | ||
63 | uint32_t *values = &(per_cpu(cookie_values, cpu)[cookiecode]); | ||
64 | |||
65 | // Can be called from interrupt handler or from work queue | ||
66 | local_irq_save(flags); | ||
67 | for (x = 0; x < MAX_COLLISIONS; x++) { | ||
68 | if (keys[x] == key) { | ||
69 | uint32_t value = values[x]; | ||
70 | for (; x > 0; x--) { | ||
71 | keys[x] = keys[x-1]; | ||
72 | values[x] = values[x-1]; | ||
73 | } | ||
74 | keys[0] = key; | ||
75 | values[0] = value; | ||
76 | retval = value; | ||
77 | break; | ||
78 | } | ||
79 | } | ||
80 | local_irq_restore(flags); | ||
81 | |||
82 | return retval; | ||
83 | } | ||
84 | |||
85 | /* | ||
86 | * Add | ||
87 | * Pre: [0][1][2][3]..[n-1] | ||
88 | * Post: [v][0][1][2]..[n-2] | ||
89 | */ | ||
90 | static void cookiemap_add(uint64_t key, uint32_t value) { | ||
91 | int cpu = smp_processor_id(); | ||
92 | int cookiecode = cookiemap_code(key); | ||
93 | uint64_t *keys = &(per_cpu(cookie_keys, cpu)[cookiecode]); | ||
94 | uint32_t *values = &(per_cpu(cookie_values, cpu)[cookiecode]); | ||
95 | int x; | ||
96 | |||
97 | for (x = MAX_COLLISIONS-1; x > 0; x--) { | ||
98 | keys[x] = keys[x-1]; | ||
99 | values[x] = keys[x-1]; | ||
100 | } | ||
101 | keys[0] = key; | ||
102 | values[0] = value; | ||
103 | } | ||
104 | |||
105 | static void translate_buffer_write_int(int cpu, unsigned int x) | ||
106 | { | ||
107 | per_cpu(translate_buffer, cpu)[per_cpu(translate_buffer_write, cpu)++] = x; | ||
108 | per_cpu(translate_buffer_write, cpu) &= translate_buffer_mask; | ||
109 | } | ||
110 | |||
111 | static unsigned int translate_buffer_read_int(int cpu) | ||
112 | { | ||
113 | unsigned int value = per_cpu(translate_buffer, cpu)[per_cpu(translate_buffer_read, cpu)++]; | ||
114 | per_cpu(translate_buffer_read, cpu) &= translate_buffer_mask; | ||
115 | return value; | ||
116 | } | ||
117 | |||
118 | static void wq_cookie_handler(struct work_struct *unused) | ||
119 | { | ||
120 | struct task_struct *task; | ||
121 | struct vm_area_struct *vma; | ||
122 | int cpu = smp_processor_id(); | ||
123 | unsigned int cookie, commit; | ||
124 | |||
125 | commit = per_cpu(translate_buffer_write, cpu); | ||
126 | while (per_cpu(translate_buffer_read, cpu) != commit) { | ||
127 | task = (struct task_struct *)translate_buffer_read_int(cpu); | ||
128 | vma = (struct vm_area_struct *)translate_buffer_read_int(cpu); | ||
129 | cookie = get_cookie(cpu, task, vma, NULL); | ||
130 | } | ||
131 | } | ||
132 | |||
133 | // Retrieve full name from proc/pid/cmdline for java processes on Android | ||
134 | static int translate_app_process(char** text, int cpu, struct task_struct * task, struct vm_area_struct *vma) | ||
135 | { | ||
136 | void *maddr; | ||
137 | unsigned int len; | ||
138 | unsigned long addr; | ||
139 | struct mm_struct *mm; | ||
140 | struct page *page = NULL; | ||
141 | struct vm_area_struct *page_vma; | ||
142 | int bytes, offset, retval = 0, ptr; | ||
143 | char * buf = per_cpu(translate_text, cpu); | ||
144 | |||
145 | // Push work into a work queue if in atomic context as the kernel functions below might sleep | ||
146 | if (in_irq()) { | ||
147 | // Check if already in buffer | ||
148 | ptr = per_cpu(translate_buffer_read, cpu); | ||
149 | while (ptr != per_cpu(translate_buffer_write, cpu)) { | ||
150 | if (per_cpu(translate_buffer, cpu)[ptr] == (int)task) | ||
151 | goto out; | ||
152 | ptr = (ptr + 2) & translate_buffer_mask; | ||
153 | } | ||
154 | |||
155 | translate_buffer_write_int(cpu, (unsigned int)task); | ||
156 | translate_buffer_write_int(cpu, (unsigned int)vma); | ||
157 | schedule_work(&cookie_work); | ||
158 | goto out; | ||
159 | } | ||
160 | |||
161 | mm = get_task_mm(task); | ||
162 | if (!mm) | ||
163 | goto out; | ||
164 | if (!mm->arg_end) | ||
165 | goto outmm; | ||
166 | addr = mm->arg_start; | ||
167 | len = mm->arg_end - mm->arg_start; | ||
168 | |||
169 | if (len > TRANSLATE_SIZE) | ||
170 | len = TRANSLATE_SIZE; | ||
171 | |||
172 | down_read(&mm->mmap_sem); | ||
173 | while (len) { | ||
174 | if (get_user_pages(task, mm, addr, 1, 0, 1, &page, &page_vma) <= 0) | ||
175 | goto outsem; | ||
176 | |||
177 | maddr = kmap(page); | ||
178 | offset = addr & (PAGE_SIZE-1); | ||
179 | bytes = len; | ||
180 | if (bytes > PAGE_SIZE - offset) | ||
181 | bytes = PAGE_SIZE - offset; | ||
182 | |||
183 | copy_from_user_page(page_vma, page, addr, buf, maddr + offset, bytes); | ||
184 | |||
185 | kunmap(page); // release page allocated by get_user_pages() | ||
186 | page_cache_release(page); | ||
187 | |||
188 | len -= bytes; | ||
189 | buf += bytes; | ||
190 | addr += bytes; | ||
191 | |||
192 | *text = per_cpu(translate_text, cpu); | ||
193 | retval = 1; | ||
194 | } | ||
195 | |||
196 | // On app_process startup, /proc/pid/cmdline is initially "zygote" then "<pre-initialized>" but changes after an initial startup period | ||
197 | if (strcmp(*text, "zygote") == 0 || strcmp(*text, "<pre-initialized>") == 0) | ||
198 | retval = 0; | ||
199 | |||
200 | outsem: | ||
201 | up_read(&mm->mmap_sem); | ||
202 | outmm: | ||
203 | mmput(mm); | ||
204 | out: | ||
205 | return retval; | ||
206 | } | ||
207 | |||
208 | static inline uint32_t get_cookie(int cpu, struct task_struct *task, struct vm_area_struct *vma, struct module *mod) | ||
209 | { | ||
210 | unsigned long flags, cookie; | ||
211 | struct path *path; | ||
212 | uint64_t key; | ||
213 | char *text; | ||
214 | |||
215 | if (mod) { | ||
216 | text = mod->name; | ||
217 | } else { | ||
218 | if (!vma || !vma->vm_file) { | ||
219 | return INVALID_COOKIE; | ||
220 | } | ||
221 | path = &vma->vm_file->f_path; | ||
222 | if (!path || !path->dentry) { | ||
223 | return INVALID_COOKIE; | ||
224 | } | ||
225 | |||
226 | text = (char*)path->dentry->d_name.name; | ||
227 | } | ||
228 | |||
229 | key = gator_chksum_crc32(text); | ||
230 | key = (key << 32) | (uint32_t)task->tgid; | ||
231 | |||
232 | cookie = cookiemap_exists(key); | ||
233 | if (cookie) { | ||
234 | return cookie; | ||
235 | } | ||
236 | |||
237 | if (strcmp(text, "app_process") == 0 && !mod) { | ||
238 | if (!translate_app_process(&text, cpu, task, vma)) | ||
239 | return INVALID_COOKIE; | ||
240 | } | ||
241 | |||
242 | // Can be called from interrupt handler or from work queue | ||
243 | local_irq_save(flags); | ||
244 | |||
245 | cookie = per_cpu(cookie_next_key, cpu)+=nr_cpu_ids; | ||
246 | cookiemap_add(key, cookie); | ||
247 | |||
248 | gator_buffer_write_packed_int(cpu, PROTOCOL_COOKIE); | ||
249 | gator_buffer_write_packed_int(cpu, cookie); | ||
250 | gator_buffer_write_string(cpu, text); | ||
251 | |||
252 | local_irq_restore(flags); | ||
253 | |||
254 | return cookie; | ||
255 | } | ||
256 | |||
257 | static int get_exec_cookie(int cpu, struct task_struct *task) | ||
258 | { | ||
259 | unsigned long cookie = NO_COOKIE; | ||
260 | struct mm_struct *mm = task->mm; | ||
261 | struct vm_area_struct *vma; | ||
262 | |||
263 | if (!mm) | ||
264 | return cookie; | ||
265 | |||
266 | for (vma = mm->mmap; vma; vma = vma->vm_next) { | ||
267 | if (!vma->vm_file) | ||
268 | continue; | ||
269 | if (!(vma->vm_flags & VM_EXECUTABLE)) | ||
270 | continue; | ||
271 | cookie = get_cookie(cpu, task, vma, NULL); | ||
272 | break; | ||
273 | } | ||
274 | |||
275 | return cookie; | ||
276 | } | ||
277 | |||
278 | static unsigned long get_address_cookie(int cpu, struct task_struct *task, unsigned long addr, off_t *offset) | ||
279 | { | ||
280 | unsigned long cookie = NO_COOKIE; | ||
281 | struct mm_struct *mm = task->mm; | ||
282 | struct vm_area_struct *vma; | ||
283 | |||
284 | if (!mm) | ||
285 | return cookie; | ||
286 | |||
287 | for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) { | ||
288 | if (addr < vma->vm_start || addr >= vma->vm_end) | ||
289 | continue; | ||
290 | |||
291 | if (vma->vm_file) { | ||
292 | cookie = get_cookie(cpu, task, vma, NULL); | ||
293 | *offset = (vma->vm_pgoff << PAGE_SHIFT) + addr - vma->vm_start; | ||
294 | } else { | ||
295 | /* must be an anonymous map */ | ||
296 | *offset = addr; | ||
297 | } | ||
298 | |||
299 | break; | ||
300 | } | ||
301 | |||
302 | if (!vma) | ||
303 | cookie = INVALID_COOKIE; | ||
304 | |||
305 | return cookie; | ||
306 | } | ||
307 | |||
308 | static int cookies_initialize(void) | ||
309 | { | ||
310 | uint32_t crc, poly; | ||
311 | int i, j, cpu, size, err = 0; | ||
312 | |||
313 | int translate_buffer_size = 512; // must be a power of 2 | ||
314 | translate_buffer_mask = translate_buffer_size / sizeof(per_cpu(translate_buffer, 0)[0]) - 1; | ||
315 | |||
316 | for_each_present_cpu(cpu) { | ||
317 | per_cpu(cookie_next_key, cpu) = nr_cpu_ids + cpu; | ||
318 | |||
319 | size = COOKIEMAP_ENTRIES * MAX_COLLISIONS * sizeof(uint64_t); | ||
320 | per_cpu(cookie_keys, cpu) = (uint64_t*)kmalloc(size, GFP_KERNEL); | ||
321 | memset(per_cpu(cookie_keys, cpu), 0, size); | ||
322 | |||
323 | size = COOKIEMAP_ENTRIES * MAX_COLLISIONS * sizeof(uint32_t); | ||
324 | per_cpu(cookie_values, cpu) = (uint32_t*)kmalloc(size, GFP_KERNEL); | ||
325 | memset(per_cpu(cookie_values, cpu), 0, size); | ||
326 | |||
327 | per_cpu(translate_buffer, cpu) = (unsigned int *)kmalloc(translate_buffer_size, GFP_KERNEL); | ||
328 | if (!per_cpu(translate_buffer, cpu)) { | ||
329 | err = -ENOMEM; | ||
330 | goto cookie_setup_error; | ||
331 | } | ||
332 | |||
333 | per_cpu(translate_buffer_write, cpu) = 0; | ||
334 | per_cpu(translate_buffer_read, cpu) = 0; | ||
335 | |||
336 | per_cpu(translate_text, cpu) = (char *)kmalloc(TRANSLATE_SIZE, GFP_KERNEL); | ||
337 | if (!per_cpu(translate_text, cpu)) { | ||
338 | err = -ENOMEM; | ||
339 | goto cookie_setup_error; | ||
340 | } | ||
341 | } | ||
342 | |||
343 | // build CRC32 table | ||
344 | poly = 0x04c11db7; | ||
345 | gator_crc32_table = (uint32_t*)kmalloc(256 * sizeof(uint32_t), GFP_KERNEL); | ||
346 | for (i = 0; i < 256; i++) { | ||
347 | crc = i; | ||
348 | for (j = 8; j > 0; j--) { | ||
349 | if (crc & 1) { | ||
350 | crc = (crc >> 1) ^ poly; | ||
351 | } else { | ||
352 | crc >>= 1; | ||
353 | } | ||
354 | } | ||
355 | gator_crc32_table[i] = crc; | ||
356 | } | ||
357 | |||
358 | cookie_setup_error: | ||
359 | return err; | ||
360 | } | ||
361 | |||
362 | static void cookies_release(void) | ||
363 | { | ||
364 | int cpu; | ||
365 | |||
366 | for_each_present_cpu(cpu) { | ||
367 | kfree(per_cpu(cookie_keys, cpu)); | ||
368 | per_cpu(cookie_keys, cpu) = NULL; | ||
369 | |||
370 | kfree(per_cpu(cookie_values, cpu)); | ||
371 | per_cpu(cookie_values, cpu) = NULL; | ||
372 | |||
373 | kfree(per_cpu(translate_buffer, cpu)); | ||
374 | per_cpu(translate_buffer, cpu) = NULL; | ||
375 | per_cpu(translate_buffer_read, cpu) = 0; | ||
376 | per_cpu(translate_buffer_write, cpu) = 0; | ||
377 | |||
378 | kfree(per_cpu(translate_text, cpu)); | ||
379 | per_cpu(translate_text, cpu) = NULL; | ||
380 | } | ||
381 | |||
382 | kfree(gator_crc32_table); | ||
383 | gator_crc32_table = NULL; | ||
384 | } | ||