root/arch/m68k/kernel/ptrace.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. get_task
  2. get_reg
  3. put_reg
  4. get_long
  5. put_long
  6. find_extend_vma
  7. read_long
  8. write_long
  9. sys_ptrace
  10. syscall_trace

   1 /*
   2  *  linux/arch/m68k/kernel/ptrace.c
   3  *
   4  *  Copyright (C) 1994 by Hamish Macdonald
   5  *  Taken from linux/kernel/ptrace.c and modified for M680x0.
   6  *  linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds
   7  *
   8  * This file is subject to the terms and conditions of the GNU General
   9  * Public License.  See the file README.legal in the main directory of
  10  * this archive for more details.
  11  */
  12 
  13 #include <stddef.h>
  14 #include <linux/kernel.h>
  15 #include <linux/sched.h>
  16 #include <linux/mm.h>
  17 #include <linux/errno.h>
  18 #include <linux/ptrace.h>
  19 #include <linux/user.h>
  20 
  21 #include <asm/segment.h>
  22 #include <asm/page.h>
  23 #include <asm/pgtable.h>
  24 #include <asm/system.h>
  25 
  26 /*
  27  * does not yet catch signals sent when the child dies.
  28  * in exit.c or in signal.c.
  29  */
  30 
  31 /* determines which bits in the SR the user has access to. */
  32 /* 1 = access 0 = no access */
  33 #define SR_MASK 0x001f
  34 
  35 /* sets the trace bits. */
  36 #define TRACE_BITS 0x8000
  37 
  38 /* Find the stack offset for a register, relative to tss.esp0. */
  39 #define PT_REG(reg)     ((long)&((struct pt_regs *)0)->reg)
  40 #define SW_REG(reg)     ((long)&((struct switch_stack *)0)->reg \
  41                          - sizeof(struct switch_stack))
  42 /* Mapping from PT_xxx to the stack offset at which the register is
  43    saved.  Notice that usp has no stack-slot and needs to be treated
  44    specially (see get_reg/put_reg below). */
  45 static int regoff[] = {
  46         PT_REG(d1), PT_REG(d2), PT_REG(d3), PT_REG(d4),
  47         PT_REG(d5), SW_REG(d6), SW_REG(d7), PT_REG(a0),
  48         PT_REG(a1), SW_REG(a2), SW_REG(a3), SW_REG(a4),
  49         SW_REG(a5), SW_REG(a6), PT_REG(d0), -1,
  50         PT_REG(orig_d0), PT_REG(sr), PT_REG(pc),
  51 };
  52 
  53 /* change a pid into a task struct. */
  54 static inline struct task_struct * get_task(int pid)
     /* [previous][next][first][last][top][bottom][index][help] */
  55 {
  56         int i;
  57 
  58         for (i = 1; i < NR_TASKS; i++) {
  59                 if (task[i] != NULL && (task[i]->pid == pid))
  60                         return task[i];
  61         }
  62         return NULL;
  63 }
  64 
  65 /*
  66  * Get contents of register REGNO in task TASK.
  67  */
  68 static inline long get_reg(struct task_struct *task, int regno)
     /* [previous][next][first][last][top][bottom][index][help] */
  69 {
  70         unsigned long *addr;
  71 
  72         if (regno == PT_USP)
  73                 addr = &task->tss.usp;
  74         else if (regno < sizeof(regoff)/sizeof(regoff[0]))
  75                 addr = (unsigned long *)(task->tss.esp0 + regoff[regno]);
  76         else
  77                 return 0;
  78         return *addr;
  79 }
  80 
  81 /*
  82  * Write contents of register REGNO in task TASK.
  83  */
  84 static inline int put_reg(struct task_struct *task, int regno,
     /* [previous][next][first][last][top][bottom][index][help] */
  85                           unsigned long data)
  86 {
  87         unsigned long *addr;
  88 
  89         if (regno == PT_USP)
  90                 addr = &task->tss.usp;
  91         else if (regno < sizeof(regoff)/sizeof(regoff[0]))
  92                 addr = (unsigned long *) (task->tss.esp0 + regoff[regno]);
  93         else
  94                 return -1;
  95         *addr = data;
  96         return 0;
  97 }
  98 
  99 /*
 100  * This routine gets a long from any process space by following the page
 101  * tables. NOTE! You should check that the long isn't on a page boundary,
 102  * and that it is in the task area before calling this: this routine does
 103  * no checking.
 104  *
 105  */
 106 static unsigned long get_long(struct task_struct * tsk, 
     /* [previous][next][first][last][top][bottom][index][help] */
 107         struct vm_area_struct * vma, unsigned long addr)
 108 {
 109         pgd_t * pgdir;
 110         pmd_t * pgmiddle;
 111         pte_t * pgtable;
 112         unsigned long page;
 113 
 114 repeat:
 115         pgdir = pgd_offset(vma->vm_mm, addr);
 116         if (pgd_none(*pgdir)) {
 117                 do_no_page(tsk, vma, addr, 0);
 118                 goto repeat;
 119         }
 120         if (pgd_bad(*pgdir)) {
 121                 printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
 122                 pgd_clear(pgdir);
 123                 return 0;
 124         }
 125         pgmiddle = pmd_offset(pgdir,addr);
 126         if (pmd_none(*pgmiddle)) {
 127                 do_no_page(tsk, vma, addr, 0);
 128                 goto repeat;
 129         }
 130         if (pmd_bad(*pgmiddle)) {
 131                 printk("ptrace: bad page directory %08lx\n",
 132                        pmd_val(*pgmiddle));
 133                 pmd_clear(pgmiddle);
 134                 return 0;
 135         }
 136         pgtable = pte_offset(pgmiddle, addr);
 137         if (!pte_present(*pgtable)) {
 138                 do_no_page(tsk, vma, addr, 0);
 139                 goto repeat;
 140         }
 141         page = pte_page(*pgtable);
 142 /* this is a hack for non-kernel-mapped video buffers and similar */
 143         if (page >= high_memory)
 144                 return 0;
 145         page += addr & ~PAGE_MASK;
 146         return *(unsigned long *) page;
 147 }
 148 
 149 /*
 150  * This routine puts a long into any process space by following the page
 151  * tables. NOTE! You should check that the long isn't on a page boundary,
 152  * and that it is in the task area before calling this: this routine does
 153  * no checking.
 154  *
 155  * Now keeps R/W state of page so that a text page stays readonly
 156  * even if a debugger scribbles breakpoints into it.  -M.U-
 157  */
 158 static void put_long(struct task_struct * tsk, struct vm_area_struct * vma, unsigned long addr,
     /* [previous][next][first][last][top][bottom][index][help] */
 159         unsigned long data)
 160 {
 161         pgd_t *pgdir;
 162         pmd_t *pgmiddle;
 163         pte_t *pgtable;
 164         unsigned long page;
 165                 
 166 repeat:
 167         pgdir = pgd_offset(vma->vm_mm, addr);
 168         if (!pgd_present(*pgdir)) {
 169                 do_no_page(tsk, vma, addr, 1);
 170                 goto repeat;
 171         }
 172         if (pgd_bad(*pgdir)) {
 173                 printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
 174                 pgd_clear(pgdir);
 175                 return;
 176         }
 177         pgmiddle = pmd_offset(pgdir,addr);
 178         if (pmd_none(*pgmiddle)) {
 179                 do_no_page(tsk, vma, addr, 1);
 180                 goto repeat;
 181         }
 182         if (pmd_bad(*pgmiddle)) {
 183                 printk("ptrace: bad page directory %08lx\n",
 184                        pmd_val(*pgmiddle));
 185                 pmd_clear(pgmiddle);
 186                 return;
 187         }
 188         pgtable = pte_offset(pgmiddle, addr);
 189         if (!pte_present(*pgtable)) {
 190                 do_no_page(tsk, vma, addr, 1);
 191                 goto repeat;
 192         }
 193         page = pte_page(*pgtable);
 194         if (!pte_write(*pgtable)) {
 195                 do_wp_page(tsk, vma, addr, 2);
 196                 goto repeat;
 197         }
 198 /* this is a hack for non-kernel-mapped video buffers and similar */
 199         if (page < high_memory) {
 200                 *(unsigned long *) (page + (addr & ~PAGE_MASK)) = data;
 201         }
 202 /* we're bypassing pagetables, so we have to set the dirty bit ourselves */
 203 /* this should also re-instate whatever read-only mode there was before */
 204         *pgtable = pte_mkdirty(mk_pte(page, vma->vm_page_prot));
 205         flush_tlb_all();
 206 }
 207 
 208 static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr)
     /* [previous][next][first][last][top][bottom][index][help] */
 209 {
 210         struct vm_area_struct * vma;
 211 
 212         addr &= PAGE_MASK;
 213         vma = find_vma(tsk,addr);
 214         if (!vma)
 215                 return NULL;
 216         if (vma->vm_start <= addr)
 217                 return vma;
 218         if (!(vma->vm_flags & VM_GROWSDOWN))
 219                 return NULL;
 220         if (vma->vm_end - addr > tsk->rlim[RLIMIT_STACK].rlim_cur)
 221                 return NULL;
 222         vma->vm_offset -= vma->vm_start - addr;
 223         vma->vm_start = addr;
 224         return vma;
 225 }
 226 
 227 /*
 228  * This routine checks the page boundaries, and that the offset is
 229  * within the task area. It then calls get_long() to read a long.
 230  */
 231 static int read_long(struct task_struct * tsk, unsigned long addr,
     /* [previous][next][first][last][top][bottom][index][help] */
 232         unsigned long * result)
 233 {
 234         struct vm_area_struct * vma = find_extend_vma(tsk, addr);
 235 
 236         if (!vma)
 237                 return -EIO;
 238         if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) {
 239                 unsigned long low,high;
 240                 struct vm_area_struct * vma_low = vma;
 241 
 242                 if (addr + sizeof(long) >= vma->vm_end) {
 243                         vma_low = vma->vm_next;
 244                         if (!vma_low || vma_low->vm_start != vma->vm_end)
 245                                 return -EIO;
 246                 }
 247                 high = get_long(tsk, vma,addr & ~(sizeof(long)-1));
 248                 low = get_long(tsk, vma_low,(addr+sizeof(long)) & ~(sizeof(long)-1));
 249                 switch (addr & (sizeof(long)-1)) {
 250                         case 3:
 251                                 low >>= 8;
 252                                 low |= high << 24;
 253                                 break;
 254                         case 2:
 255                                 low >>= 16;
 256                                 low |= high << 16;
 257                                 break;
 258                         case 1:
 259                                 low >>= 24;
 260                                 low |= high << 8;
 261                                 break;
 262                 }
 263                 *result = low;
 264         } else
 265                 *result = get_long(tsk, vma,addr);
 266         return 0;
 267 }
 268 
 269 /*
 270  * This routine checks the page boundaries, and that the offset is
 271  * within the task area. It then calls put_long() to write a long.
 272  */
 273 static int write_long(struct task_struct * tsk, unsigned long addr,
     /* [previous][next][first][last][top][bottom][index][help] */
 274         unsigned long data)
 275 {
 276         struct vm_area_struct * vma = find_extend_vma(tsk, addr);
 277 
 278         if (!vma)
 279                 return -EIO;
 280         if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) {
 281                 unsigned long low,high;
 282                 struct vm_area_struct * vma_low = vma;
 283 
 284                 if (addr + sizeof(long) >= vma->vm_end) {
 285                         vma_low = vma->vm_next;
 286                         if (!vma_low || vma_low->vm_start != vma->vm_end)
 287                                 return -EIO;
 288                 }
 289                 high = get_long(tsk, vma,addr & ~(sizeof(long)-1));
 290                 low = get_long(tsk, vma_low,(addr+sizeof(long)) & ~(sizeof(long)-1));
 291                 switch (addr & (sizeof(long)-1)) {
 292                         case 0: /* shouldn't happen, but safety first */
 293                                 high = data;
 294                                 break;
 295                         case 3:
 296                                 low &= 0x000000ff;
 297                                 low |= data << 8;
 298                                 high &= ~0xff;
 299                                 high |= data >> 24;
 300                                 break;
 301                         case 2:
 302                                 low &= 0x0000ffff;
 303                                 low |= data << 16;
 304                                 high &= ~0xffff;
 305                                 high |= data >> 16;
 306                                 break;
 307                         case 1:
 308                                 low &= 0x00ffffff;
 309                                 low |= data << 24;
 310                                 high &= ~0xffffff;
 311                                 high |= data >> 8;
 312                                 break;
 313                 }
 314                 put_long(tsk, vma,addr & ~(sizeof(long)-1),high);
 315                 put_long(tsk, vma_low,(addr+sizeof(long)) & ~(sizeof(long)-1),low);
 316         } else
 317                 put_long(tsk, vma,addr,data);
 318         return 0;
 319 }
 320 
 321 asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
     /* [previous][next][first][last][top][bottom][index][help] */
 322 {
 323         struct task_struct *child;
 324         struct user * dummy;
 325 
 326         dummy = NULL;
 327 
 328         if (request == PTRACE_TRACEME) {
 329                 /* are we already being traced? */
 330                 if (current->flags & PF_PTRACED)
 331                         return -EPERM;
 332                 /* set the ptrace bit in the process flags. */
 333                 current->flags |= PF_PTRACED;
 334                 return 0;
 335         }
 336         if (pid == 1)           /* you may not mess with init */
 337                 return -EPERM;
 338         if (!(child = get_task(pid)))
 339                 return -ESRCH;
 340         if (request == PTRACE_ATTACH) {
 341                 if (child == current)
 342                         return -EPERM;
 343                 if ((!child->dumpable ||
 344                     (current->uid != child->euid) ||
 345                     (current->uid != child->uid) ||
 346                     (current->gid != child->egid) ||
 347                     (current->gid != child->gid)) && !suser())
 348                         return -EPERM;
 349                 /* the same process cannot be attached many times */
 350                 if (child->flags & PF_PTRACED)
 351                         return -EPERM;
 352                 child->flags |= PF_PTRACED;
 353                 if (child->p_pptr != current) {
 354                         REMOVE_LINKS(child);
 355                         child->p_pptr = current;
 356                         SET_LINKS(child);
 357                 }
 358                 send_sig(SIGSTOP, child, 1);
 359                 return 0;
 360         }
 361         if (!(child->flags & PF_PTRACED))
 362                 return -ESRCH;
 363         if (child->state != TASK_STOPPED) {
 364                 if (request != PTRACE_KILL)
 365                         return -ESRCH;
 366         }
 367         if (child->p_pptr != current)
 368                 return -ESRCH;
 369 
 370         switch (request) {
 371         /* when I and D space are separate, these will need to be fixed. */
 372                 case PTRACE_PEEKTEXT: /* read word at location addr. */ 
 373                 case PTRACE_PEEKDATA: {
 374                         unsigned long tmp;
 375                         int res;
 376 
 377                         res = read_long(child, addr, &tmp);
 378                         if (res < 0)
 379                                 return res;
 380                         res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
 381                         if (!res)
 382                                 put_user(tmp, (unsigned long *) data);
 383                         return res;
 384                 }
 385 
 386         /* read the word at location addr in the USER area. */
 387                 case PTRACE_PEEKUSR: {
 388                         unsigned long tmp;
 389                         int res;
 390                         
 391                         if ((addr & 3) || addr < 0 || addr >= sizeof(struct user))
 392                                 return -EIO;
 393                         
 394                         res = verify_area(VERIFY_WRITE, (void *) data,
 395                                           sizeof(long));
 396                         if (res)
 397                                 return res;
 398                         tmp = 0;  /* Default return condition */
 399                         addr = addr >> 2; /* temporary hack. */
 400                         if (addr < 19) {
 401                                 tmp = get_reg(child, addr);
 402                                 if (addr == PT_SR)
 403                                         tmp >>= 16;
 404                         }
 405                         else if (addr >= 21 && addr < 49)
 406                                 tmp = child->tss.fp[addr - 21];
 407                         else
 408                                 return -EIO;
 409                         put_user(tmp,(unsigned long *) data);
 410                         return 0;
 411                 }
 412 
 413       /* when I and D space are separate, this will have to be fixed. */
 414                 case PTRACE_POKETEXT: /* write the word at location addr. */
 415                 case PTRACE_POKEDATA:
 416                         return write_long(child,addr,data);
 417 
 418                 case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
 419                         if ((addr & 3) || addr < 0 || addr >= sizeof(struct user))
 420                                 return -EIO;
 421 
 422                         addr = addr >> 2; /* temporary hack. */
 423                             
 424                         if (addr == PT_ORIG_D0)
 425                                 return -EIO;
 426                         if (addr == PT_SR) {
 427                                 data &= SR_MASK;
 428                                 data <<= 16;
 429                                 data |= get_reg(child, PT_SR) & ~(SR_MASK << 16);
 430                         }
 431                         if (addr < 19) {
 432                                 if (put_reg(child, addr, data))
 433                                         return -EIO;
 434                                 return 0;
 435                         }
 436                         if (addr >= 21 && addr < 48)
 437                         {
 438                                 child->tss.fp[addr - 21] = data;
 439                                 return 0;
 440                         }
 441                         return -EIO;
 442 
 443                 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
 444                 case PTRACE_CONT: { /* restart after signal. */
 445                         long tmp;
 446 
 447                         if ((unsigned long) data >= NSIG)
 448                                 return -EIO;
 449                         if (request == PTRACE_SYSCALL)
 450                                 child->flags |= PF_TRACESYS;
 451                         else
 452                                 child->flags &= ~PF_TRACESYS;
 453                         child->exit_code = data;
 454                         wake_up_process(child);
 455                         /* make sure the single step bit is not set. */
 456                         tmp = get_reg(child, PT_SR) & ~(TRACE_BITS << 16);
 457                         put_reg(child, PT_SR, tmp);
 458                         return 0;
 459                 }
 460 
 461 /*
 462  * make the child exit.  Best I can do is send it a sigkill. 
 463  * perhaps it should be put in the status that it wants to 
 464  * exit.
 465  */
 466                 case PTRACE_KILL: {
 467                         long tmp;
 468 
 469                         if (child->state == TASK_ZOMBIE) /* already dead */
 470                                 return 0;
 471                         wake_up_process(child);
 472                         child->exit_code = SIGKILL;
 473         /* make sure the single step bit is not set. */
 474                         tmp = get_reg(child, PT_SR) & ~(TRACE_BITS << 16);
 475                         put_reg(child, PT_SR, tmp);
 476                         return 0;
 477                 }
 478 
 479                 case PTRACE_SINGLESTEP: {  /* set the trap flag. */
 480                         long tmp;
 481 
 482                         if ((unsigned long) data >= NSIG)
 483                                 return -EIO;
 484                         child->flags &= ~PF_TRACESYS;
 485                         tmp = get_reg(child, PT_SR) | (TRACE_BITS << 16);
 486                         put_reg(child, PT_SR, tmp);
 487 
 488                         wake_up_process(child);
 489                         child->exit_code = data;
 490         /* give it a chance to run. */
 491                         return 0;
 492                 }
 493 
 494                 case PTRACE_DETACH: { /* detach a process that was attached. */
 495                         long tmp;
 496 
 497                         if ((unsigned long) data >= NSIG)
 498                                 return -EIO;
 499                         child->flags &= ~(PF_PTRACED|PF_TRACESYS);
 500                         wake_up_process(child);
 501                         child->exit_code = data;
 502                         REMOVE_LINKS(child);
 503                         child->p_pptr = child->p_opptr;
 504                         SET_LINKS(child);
 505                         /* make sure the single step bit is not set. */
 506                         tmp = get_reg(child, PT_SR) & ~(TRACE_BITS << 16);
 507                         put_reg(child, PT_SR, tmp);
 508                         return 0;
 509                 }
 510 
 511                 default:
 512                         return -EIO;
 513         }
 514 }
 515 
 516 asmlinkage void syscall_trace(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 517 {
 518         if ((current->flags & (PF_PTRACED|PF_TRACESYS))
 519                         != (PF_PTRACED|PF_TRACESYS))
 520                 return;
 521         current->exit_code = SIGTRAP;
 522         current->state = TASK_STOPPED;
 523         notify_parent(current);
 524         schedule();
 525         /*
 526          * this isn't the same as continuing with a signal, but it will do
 527          * for normal use.  strace only continues with a signal if the
 528          * stopping signal is not SIGTRAP.  -brl
 529          */
 530         if (current->exit_code)
 531                 current->signal |= (1 << (current->exit_code - 1));
 532         current->exit_code = 0;
 533         return;
 534 }

/* [previous][next][first][last][top][bottom][index][help] */