Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

FRV: Implement new-style ptrace

Implement the new-style ptrace for FRV, including adding appropriate
tracehooks.

Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

David Howells and committed by
Linus Torvalds
4a3b9893 24ceb7e8

+365 -162
+1
arch/frv/Kconfig
··· 6 6 bool 7 7 default y 8 8 select HAVE_IDE 9 + select HAVE_ARCH_TRACEHOOK 9 10 10 11 config ZONE_DMA 11 12 bool
+1
arch/frv/include/asm/elf.h
··· 116 116 } while(0) 117 117 118 118 #define USE_ELF_CORE_DUMP 119 + #define CORE_DUMP_USE_REGSET 119 120 #define ELF_FDPIC_CORE_EFLAGS EF_FRV_FDPIC 120 121 #define ELF_EXEC_PAGESIZE 16384 121 122
+10 -1
arch/frv/include/asm/ptrace.h
··· 65 65 #ifdef __KERNEL__ 66 66 #ifndef __ASSEMBLY__ 67 67 68 + struct task_struct; 69 + 68 70 /* 69 71 * we dedicate GR28 to keeping a pointer to the current exception frame 70 72 * - gr28 is destroyed on entry to the kernel from userspace ··· 75 73 76 74 #define user_mode(regs) (!((regs)->psr & PSR_S)) 77 75 #define instruction_pointer(regs) ((regs)->pc) 76 + #define user_stack_pointer(regs) ((regs)->sp) 78 77 79 78 extern unsigned long user_stack(const struct pt_regs *); 80 79 extern void show_regs(struct pt_regs *); 81 80 #define profile_pc(regs) ((regs)->pc) 82 - #endif 81 + 82 + #define task_pt_regs(task) ((task)->thread.frame0) 83 + 84 + #define arch_has_single_step() (1) 85 + extern void user_enable_single_step(struct task_struct *); 86 + extern void user_disable_single_step(struct task_struct *); 83 87 84 88 #endif /* !__ASSEMBLY__ */ 89 + #endif /* __KERNEL__ */ 85 90 #endif /* _ASM_PTRACE_H */
+123
arch/frv/include/asm/syscall.h
··· 1 + /* syscall parameter access functions 2 + * 3 + * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved. 4 + * Written by David Howells (dhowells@redhat.com) 5 + * 6 + * This program is free software; you can redistribute it and/or 7 + * modify it under the terms of the GNU General Public Licence 8 + * as published by the Free Software Foundation; either version 9 + * 2 of the Licence, or (at your option) any later version. 10 + */ 11 + 12 + #ifndef _ASM_SYSCALL_H 13 + #define _ASM_SYSCALL_H 14 + 15 + #include <linux/err.h> 16 + #include <asm/ptrace.h> 17 + 18 + /* 19 + * Get the system call number or -1 20 + */ 21 + static inline long syscall_get_nr(struct task_struct *task, 22 + struct pt_regs *regs) 23 + { 24 + return regs->syscallno; 25 + } 26 + 27 + /* 28 + * Restore the clobbered GR8 register 29 + * (1st syscall arg was overwritten with syscall return or error) 30 + */ 31 + static inline void syscall_rollback(struct task_struct *task, 32 + struct pt_regs *regs) 33 + { 34 + regs->gr8 = regs->orig_gr8; 35 + } 36 + 37 + /* 38 + * See if the syscall return value is an error, returning it if it is and 0 if 39 + * not 40 + */ 41 + static inline long syscall_get_error(struct task_struct *task, 42 + struct pt_regs *regs) 43 + { 44 + return IS_ERR_VALUE(regs->gr8) ? regs->gr8 : 0; 45 + } 46 + 47 + /* 48 + * Get the syscall return value 49 + */ 50 + static inline long syscall_get_return_value(struct task_struct *task, 51 + struct pt_regs *regs) 52 + { 53 + return regs->gr8; 54 + } 55 + 56 + /* 57 + * Set the syscall return value 58 + */ 59 + static inline void syscall_set_return_value(struct task_struct *task, 60 + struct pt_regs *regs, 61 + int error, long val) 62 + { 63 + if (error) 64 + regs->gr8 = -error; 65 + else 66 + regs->gr8 = val; 67 + } 68 + 69 + /* 70 + * Retrieve the system call arguments 71 + */ 72 + static inline void syscall_get_arguments(struct task_struct *task, 73 + struct pt_regs *regs, 74 + unsigned int i, unsigned int n, 75 + unsigned long *args) 76 + { 77 + /* 78 + * Do this simply for now. If we need to start supporting 79 + * fetching arguments from arbitrary indices, this will need some 80 + * extra logic. Presently there are no in-tree users that depend 81 + * on this behaviour. 82 + */ 83 + BUG_ON(i); 84 + 85 + /* Argument pattern is: GR8, GR9, GR10, GR11, GR12, GR13 */ 86 + switch (n) { 87 + case 6: args[5] = regs->gr13; 88 + case 5: args[4] = regs->gr12; 89 + case 4: args[3] = regs->gr11; 90 + case 3: args[2] = regs->gr10; 91 + case 2: args[1] = regs->gr9; 92 + case 1: args[0] = regs->gr8; 93 + break; 94 + default: 95 + BUG(); 96 + } 97 + } 98 + 99 + /* 100 + * Alter the system call arguments 101 + */ 102 + static inline void syscall_set_arguments(struct task_struct *task, 103 + struct pt_regs *regs, 104 + unsigned int i, unsigned int n, 105 + const unsigned long *args) 106 + { 107 + /* Same note as above applies */ 108 + BUG_ON(i); 109 + 110 + switch (n) { 111 + case 6: regs->gr13 = args[5]; 112 + case 5: regs->gr12 = args[4]; 113 + case 4: regs->gr11 = args[3]; 114 + case 3: regs->gr10 = args[2]; 115 + case 2: regs->gr9 = args[1]; 116 + case 1: regs->gr8 = args[0]; 117 + break; 118 + default: 119 + BUG(); 120 + } 121 + } 122 + 123 + #endif /* _ASM_SYSCALL_H */
+5 -7
arch/frv/kernel/entry.S
··· 1149 1149 # perform syscall entry tracing 1150 1150 __syscall_trace_entry: 1151 1151 LEDS 0x6320 1152 - setlos.p #0,gr8 1153 - call do_syscall_trace 1152 + call syscall_trace_entry 1154 1153 1155 - ldi @(gr28,#REG_SYSCALLNO),gr7 1156 - lddi @(gr28,#REG_GR(8)) ,gr8 1154 + lddi.p @(gr28,#REG_GR(8)) ,gr8 1155 + ori gr8,#0,gr7 ; syscall_trace_entry() returned new syscallno 1157 1156 lddi @(gr28,#REG_GR(10)),gr10 1158 1157 lddi.p @(gr28,#REG_GR(12)),gr12 1159 1158 ··· 1167 1168 beq icc0,#1,__entry_work_pending 1168 1169 1169 1170 movsg psr,gr23 1170 - andi gr23,#~PSR_PIL,gr23 ; could let do_syscall_trace() call schedule() 1171 + andi gr23,#~PSR_PIL,gr23 ; could let syscall_trace_exit() call schedule() 1171 1172 movgs gr23,psr 1172 1173 1173 - setlos.p #1,gr8 1174 - call do_syscall_trace 1174 + call syscall_trace_exit 1175 1175 bra __entry_resume_userspace 1176 1176 1177 1177 __syscall_badsys:
+220 -154
arch/frv/kernel/ptrace.c
··· 19 19 #include <linux/user.h> 20 20 #include <linux/security.h> 21 21 #include <linux/signal.h> 22 + #include <linux/regset.h> 23 + #include <linux/elf.h> 24 + #include <linux/tracehook.h> 22 25 23 26 #include <asm/uaccess.h> 24 27 #include <asm/page.h> ··· 34 31 * does not yet catch signals sent when the child dies. 35 32 * in exit.c or in signal.c. 36 33 */ 34 + 35 + /* 36 + * retrieve the contents of FRV userspace general registers 37 + */ 38 + static int genregs_get(struct task_struct *target, 39 + const struct user_regset *regset, 40 + unsigned int pos, unsigned int count, 41 + void *kbuf, void __user *ubuf) 42 + { 43 + const struct user_int_regs *iregs = &target->thread.user->i; 44 + int ret; 45 + 46 + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, 47 + iregs, 0, sizeof(*iregs)); 48 + if (ret < 0) 49 + return ret; 50 + 51 + return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, 52 + sizeof(*iregs), -1); 53 + } 54 + 55 + /* 56 + * update the contents of the FRV userspace general registers 57 + */ 58 + static int genregs_set(struct task_struct *target, 59 + const struct user_regset *regset, 60 + unsigned int pos, unsigned int count, 61 + const void *kbuf, const void __user *ubuf) 62 + { 63 + struct user_int_regs *iregs = &target->thread.user->i; 64 + unsigned int offs_gr0, offs_gr1; 65 + int ret; 66 + 67 + /* not allowed to set PSR or __status */ 68 + if (pos < offsetof(struct user_int_regs, psr) + sizeof(long) && 69 + pos + count > offsetof(struct user_int_regs, psr)) 70 + return -EIO; 71 + 72 + if (pos < offsetof(struct user_int_regs, __status) + sizeof(long) && 73 + pos + count > offsetof(struct user_int_regs, __status)) 74 + return -EIO; 75 + 76 + /* set the control regs */ 77 + offs_gr0 = offsetof(struct user_int_regs, gr[0]); 78 + offs_gr1 = offsetof(struct user_int_regs, gr[1]); 79 + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, 80 + iregs, 0, offs_gr0); 81 + if (ret < 0) 82 + return ret; 83 + 84 + /* skip GR0/TBR */ 85 + ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 86 + offs_gr0, offs_gr1); 87 + if (ret < 0) 88 + return ret; 89 + 90 + /* set the general regs */ 91 + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, 92 + &iregs->gr[1], offs_gr1, sizeof(*iregs)); 93 + if (ret < 0) 94 + return ret; 95 + 96 + return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 97 + sizeof(*iregs), -1); 98 + } 99 + 100 + /* 101 + * retrieve the contents of FRV userspace FP/Media registers 102 + */ 103 + static int fpmregs_get(struct task_struct *target, 104 + const struct user_regset *regset, 105 + unsigned int pos, unsigned int count, 106 + void *kbuf, void __user *ubuf) 107 + { 108 + const struct user_fpmedia_regs *fpregs = &target->thread.user->f; 109 + int ret; 110 + 111 + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, 112 + fpregs, 0, sizeof(*fpregs)); 113 + if (ret < 0) 114 + return ret; 115 + 116 + return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, 117 + sizeof(*fpregs), -1); 118 + } 119 + 120 + /* 121 + * update the contents of the FRV userspace FP/Media registers 122 + */ 123 + static int fpmregs_set(struct task_struct *target, 124 + const struct user_regset *regset, 125 + unsigned int pos, unsigned int count, 126 + const void *kbuf, const void __user *ubuf) 127 + { 128 + struct user_fpmedia_regs *fpregs = &target->thread.user->f; 129 + int ret; 130 + 131 + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, 132 + fpregs, 0, sizeof(*fpregs)); 133 + if (ret < 0) 134 + return ret; 135 + 136 + return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 137 + sizeof(*fpregs), -1); 138 + } 139 + 140 + /* 141 + * determine if the FP/Media registers have actually been used 142 + */ 143 + static int fpmregs_active(struct task_struct *target, 144 + const struct user_regset *regset) 145 + { 146 + return tsk_used_math(target) ? regset->n : 0; 147 + } 148 + 149 + /* 150 + * Define the register sets available on the FRV under Linux 151 + */ 152 + enum frv_regset { 153 + REGSET_GENERAL, 154 + REGSET_FPMEDIA, 155 + }; 156 + 157 + static const struct user_regset frv_regsets[] = { 158 + /* 159 + * General register format is: 160 + * PSR, ISR, CCR, CCCR, LR, LCR, PC, (STATUS), SYSCALLNO, ORIG_G8 161 + * GNER0-1, IACC0, TBR, GR1-63 162 + */ 163 + [REGSET_GENERAL] = { 164 + .core_note_type = NT_PRSTATUS, 165 + .n = ELF_NGREG, 166 + .size = sizeof(long), 167 + .align = sizeof(long), 168 + .get = genregs_get, 169 + .set = genregs_set, 170 + }, 171 + /* 172 + * FPU/Media register format is: 173 + * FR0-63, FNER0-1, MSR0-1, ACC0-7, ACCG0-8, FSR 174 + */ 175 + [REGSET_FPMEDIA] = { 176 + .core_note_type = NT_PRFPREG, 177 + .n = sizeof(struct user_fpmedia_regs) / sizeof(long), 178 + .size = sizeof(long), 179 + .align = sizeof(long), 180 + .get = fpmregs_get, 181 + .set = fpmregs_set, 182 + .active = fpmregs_active, 183 + }, 184 + }; 185 + 186 + static const struct user_regset_view user_frv_native_view = { 187 + .name = "frv", 188 + .e_machine = EM_FRV, 189 + .regsets = frv_regsets, 190 + .n = ARRAY_SIZE(frv_regsets), 191 + }; 192 + 193 + const struct user_regset_view *task_user_regset_view(struct task_struct *task) 194 + { 195 + return &user_frv_native_view; 196 + } 37 197 38 198 /* 39 199 * Get contents of register REGNO in task TASK. ··· 235 69 } 236 70 237 71 /* 238 - * check that an address falls within the bounds of the target process's memory 239 - * mappings 240 - */ 241 - static inline int is_user_addr_valid(struct task_struct *child, 242 - unsigned long start, unsigned long len) 243 - { 244 - #ifdef CONFIG_MMU 245 - if (start >= PAGE_OFFSET || len > PAGE_OFFSET - start) 246 - return -EIO; 247 - return 0; 248 - #else 249 - struct vm_area_struct *vma; 250 - 251 - vma = find_vma(child->mm, start); 252 - if (vma && start >= vma->vm_start && start + len <= vma->vm_end) 253 - return 0; 254 - 255 - return -EIO; 256 - #endif 257 - } 258 - 259 - /* 260 72 * Called by kernel/ptrace.c when detaching.. 261 73 * 262 74 * Control h/w single stepping 263 75 */ 264 - void ptrace_disable(struct task_struct *child) 76 + void user_enable_single_step(struct task_struct *child) 77 + { 78 + child->thread.frame0->__status |= REG__STATUS_STEP; 79 + } 80 + 81 + void user_disable_single_step(struct task_struct *child) 265 82 { 266 83 child->thread.frame0->__status &= ~REG__STATUS_STEP; 267 84 } 268 85 269 - void ptrace_enable(struct task_struct *child) 86 + void ptrace_disable(struct task_struct *child) 270 87 { 271 - child->thread.frame0->__status |= REG__STATUS_STEP; 88 + user_disable_single_step(child); 272 89 } 273 90 274 91 long arch_ptrace(struct task_struct *child, long request, long addr, long data) ··· 260 111 int ret; 261 112 262 113 switch (request) { 263 - /* when I and D space are separate, these will need to be fixed. */ 264 - case PTRACE_PEEKTEXT: /* read word at location addr. */ 265 - case PTRACE_PEEKDATA: 266 - ret = -EIO; 267 - if (is_user_addr_valid(child, addr, sizeof(tmp)) < 0) 268 - break; 269 - ret = generic_ptrace_peekdata(child, addr, data); 270 - break; 271 - 272 114 /* read the word at location addr in the USER area. */ 273 115 case PTRACE_PEEKUSR: { 274 116 tmp = 0; ··· 303 163 break; 304 164 } 305 165 306 - /* when I and D space are separate, this will have to be fixed. */ 307 - case PTRACE_POKETEXT: /* write the word at location addr. */ 308 - case PTRACE_POKEDATA: 309 - ret = -EIO; 310 - if (is_user_addr_valid(child, addr, sizeof(tmp)) < 0) 311 - break; 312 - ret = generic_ptrace_pokedata(child, addr, data); 313 - break; 314 - 315 166 case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ 316 167 ret = -EIO; 317 168 if ((addr & 3) || addr < 0) ··· 310 179 311 180 ret = 0; 312 181 switch (addr >> 2) { 313 - case 0 ... PT__END-1: 182 + case 0 ... PT__END - 1: 314 183 ret = put_reg(child, addr >> 2, data); 315 184 break; 316 185 ··· 320 189 } 321 190 break; 322 191 323 - case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ 324 - case PTRACE_CONT: /* restart after signal. */ 325 - ret = -EIO; 326 - if (!valid_signal(data)) 327 - break; 328 - if (request == PTRACE_SYSCALL) 329 - set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); 330 - else 331 - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); 332 - child->exit_code = data; 333 - ptrace_disable(child); 334 - wake_up_process(child); 335 - ret = 0; 336 - break; 192 + case PTRACE_GETREGS: /* Get all integer regs from the child. */ 193 + return copy_regset_to_user(child, &user_frv_native_view, 194 + REGSET_GENERAL, 195 + 0, sizeof(child->thread.user->i), 196 + (void __user *)data); 337 197 338 - /* make the child exit. Best I can do is send it a sigkill. 339 - * perhaps it should be put in the status that it wants to 340 - * exit. 341 - */ 342 - case PTRACE_KILL: 343 - ret = 0; 344 - if (child->exit_state == EXIT_ZOMBIE) /* already dead */ 345 - break; 346 - child->exit_code = SIGKILL; 347 - clear_tsk_thread_flag(child, TIF_SINGLESTEP); 348 - ptrace_disable(child); 349 - wake_up_process(child); 350 - break; 198 + case PTRACE_SETREGS: /* Set all integer regs in the child. */ 199 + return copy_regset_from_user(child, &user_frv_native_view, 200 + REGSET_GENERAL, 201 + 0, sizeof(child->thread.user->i), 202 + (const void __user *)data); 351 203 352 - case PTRACE_SINGLESTEP: /* set the trap flag. */ 353 - ret = -EIO; 354 - if (!valid_signal(data)) 355 - break; 356 - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); 357 - ptrace_enable(child); 358 - child->exit_code = data; 359 - wake_up_process(child); 360 - ret = 0; 361 - break; 204 + case PTRACE_GETFPREGS: /* Get the child FP/Media state. */ 205 + return copy_regset_to_user(child, &user_frv_native_view, 206 + REGSET_FPMEDIA, 207 + 0, sizeof(child->thread.user->f), 208 + (void __user *)data); 362 209 363 - case PTRACE_DETACH: /* detach a process that was attached. */ 364 - ret = ptrace_detach(child, data); 365 - break; 366 - 367 - case PTRACE_GETREGS: { /* Get all integer regs from the child. */ 368 - int i; 369 - for (i = 0; i < PT__GPEND; i++) { 370 - tmp = get_reg(child, i); 371 - if (put_user(tmp, (unsigned long *) data)) { 372 - ret = -EFAULT; 373 - break; 374 - } 375 - data += sizeof(long); 376 - } 377 - ret = 0; 378 - break; 379 - } 380 - 381 - case PTRACE_SETREGS: { /* Set all integer regs in the child. */ 382 - int i; 383 - for (i = 0; i < PT__GPEND; i++) { 384 - if (get_user(tmp, (unsigned long *) data)) { 385 - ret = -EFAULT; 386 - break; 387 - } 388 - put_reg(child, i, tmp); 389 - data += sizeof(long); 390 - } 391 - ret = 0; 392 - break; 393 - } 394 - 395 - case PTRACE_GETFPREGS: { /* Get the child FP/Media state. */ 396 - ret = 0; 397 - if (copy_to_user((void *) data, 398 - &child->thread.user->f, 399 - sizeof(child->thread.user->f))) 400 - ret = -EFAULT; 401 - break; 402 - } 403 - 404 - case PTRACE_SETFPREGS: { /* Set the child FP/Media state. */ 405 - ret = 0; 406 - if (copy_from_user(&child->thread.user->f, 407 - (void *) data, 408 - sizeof(child->thread.user->f))) 409 - ret = -EFAULT; 410 - break; 411 - } 210 + case PTRACE_SETFPREGS: /* Set the child FP/Media state. */ 211 + return copy_regset_from_user(child, &user_frv_native_view, 212 + REGSET_FPMEDIA, 213 + 0, sizeof(child->thread.user->f), 214 + (const void __user *)data); 412 215 413 216 case PTRACE_GETFDPIC: 414 217 tmp = 0; ··· 365 300 break; 366 301 367 302 default: 368 - ret = -EIO; 303 + ret = ptrace_request(child, request, addr, data); 369 304 break; 370 305 } 371 306 return ret; 372 307 } 373 308 374 - asmlinkage void do_syscall_trace(int leaving) 309 + /* 310 + * handle tracing of system call entry 311 + * - return the revised system call number or ULONG_MAX to cause ENOSYS 312 + */ 313 + asmlinkage unsigned long syscall_trace_entry(void) 375 314 { 376 - if (!test_thread_flag(TIF_SYSCALL_TRACE)) 377 - return; 378 - 379 - if (!(current->ptrace & PT_PTRACED)) 380 - return; 381 - 382 - /* we need to indicate entry or exit to strace */ 383 - if (leaving) 384 - __frame->__status |= REG__STATUS_SYSC_EXIT; 385 - else 386 - __frame->__status |= REG__STATUS_SYSC_ENTRY; 387 - 388 - ptrace_notify(SIGTRAP); 389 - 390 - /* 391 - * this isn't the same as continuing with a signal, but it will do 392 - * for normal use. strace only continues with a signal if the 393 - * stopping signal is not SIGTRAP. -brl 394 - */ 395 - if (current->exit_code) { 396 - send_sig(current->exit_code, current, 1); 397 - current->exit_code = 0; 315 + __frame->__status |= REG__STATUS_SYSC_ENTRY; 316 + if (tracehook_report_syscall_entry(__frame)) { 317 + /* tracing decided this syscall should not happen, so 318 + * We'll return a bogus call number to get an ENOSYS 319 + * error, but leave the original number in 320 + * __frame->syscallno 321 + */ 322 + return ULONG_MAX; 398 323 } 324 + 325 + return __frame->syscallno; 326 + } 327 + 328 + /* 329 + * handle tracing of system call exit 330 + */ 331 + asmlinkage void syscall_trace_exit(void) 332 + { 333 + __frame->__status |= REG__STATUS_SYSC_EXIT; 334 + tracehook_report_syscall_exit(__frame, 0); 399 335 }
+5
arch/frv/kernel/signal.c
··· 21 21 #include <linux/unistd.h> 22 22 #include <linux/personality.h> 23 23 #include <linux/freezer.h> 24 + #include <linux/tracehook.h> 24 25 #include <asm/ucontext.h> 25 26 #include <asm/uaccess.h> 26 27 #include <asm/cacheflush.h> ··· 517 516 * clear the TIF_RESTORE_SIGMASK flag */ 518 517 if (test_thread_flag(TIF_RESTORE_SIGMASK)) 519 518 clear_thread_flag(TIF_RESTORE_SIGMASK); 519 + 520 + tracehook_signal_handler(signr, &info, &ka, __frame, 521 + test_thread_flag(TIF_SINGLESTEP)); 520 522 } 521 523 522 524 return; ··· 571 567 /* deal with notification on about to resume userspace execution */ 572 568 if (thread_info_flags & _TIF_NOTIFY_RESUME) { 573 569 clear_thread_flag(TIF_NOTIFY_RESUME); 570 + tracehook_notify_resume(__frame); 574 571 } 575 572 576 573 } /* end do_notify_resume() */