patch-2.4.20 linux-2.4.20/arch/s390x/kernel/ptrace.c
Next file: linux-2.4.20/arch/s390x/kernel/s390_ext.c
Previous file: linux-2.4.20/arch/s390x/kernel/process.c
Back to the patch index
Back to the overall index
- Lines: 636
- Date:
Thu Nov 28 15:53:11 2002
- Orig file:
linux-2.4.19/arch/s390x/kernel/ptrace.c
- Orig date:
Fri Aug 2 17:39:43 2002
diff -urN linux-2.4.19/arch/s390x/kernel/ptrace.c linux-2.4.20/arch/s390x/kernel/ptrace.c
@@ -38,6 +38,12 @@
#include <asm/pgalloc.h>
#include <asm/system.h>
#include <asm/uaccess.h>
+#ifdef CONFIG_S390_SUPPORT
+#include "linux32.h"
+#else
+#define parent_31bit 0
+#endif
+
void FixPerRegisters(struct task_struct *task)
{
@@ -122,8 +128,8 @@
retval = clear_user(copyptr, len);
else
retval = copy_to_user(copyptr,realuserptr,len);
- retval = (retval == -EFAULT) ? -EIO : 0;
}
+ retval = retval ? -EFAULT : 0;
} else {
if (writeuser)
memcpy(realuserptr, copyptr, len);
@@ -135,48 +141,180 @@
return retval;
}
+#ifdef CONFIG_S390_SUPPORT
+
+typedef struct
+{
+ __u32 cr[3];
+} per_cr_words32 __attribute__((packed));
+
+typedef struct
+{
+ __u16 perc_atmid; /* 0x096 */
+ __u32 address; /* 0x098 */
+ __u8 access_id; /* 0x0a1 */
+} per_lowcore_words32 __attribute__((packed));
+
+typedef struct
+{
+ union {
+ per_cr_words32 words;
+ } control_regs __attribute__((packed));
+ /*
+ * Use these flags instead of setting em_instruction_fetch
+ * directly they are used so that single stepping can be
+ * switched on & off while not affecting other tracing
+ */
+ unsigned single_step : 1;
+ unsigned instruction_fetch : 1;
+ unsigned : 30;
+ /*
+ * These addresses are copied into cr10 & cr11 if single
+ * stepping is switched off
+ */
+ __u32 starting_addr;
+ __u32 ending_addr;
+ union {
+ per_lowcore_words32 words;
+ } lowcore;
+} per_struct32 __attribute__((packed));
+
+struct user_regs_struct32
+{
+ _psw_t32 psw;
+ u32 gprs[NUM_GPRS];
+ u32 acrs[NUM_ACRS];
+ u32 orig_gpr2;
+ s390_fp_regs fp_regs;
+ /*
+ * These per registers are in here so that gdb can modify them
+ * itself as there is no "official" ptrace interface for hardware
+ * watchpoints. This is the way intel does it.
+ */
+ per_struct32 per_info;
+ u32 ieee_instruction_pointer;
+ /* Used to give failing instruction back to user for ieee exceptions */
+};
+
+struct user32 {
+ /* We start with the registers, to mimic the way that "memory" is returned
+ from the ptrace(3,...) function. */
+ struct user_regs_struct32 regs; /* Where the registers are actually stored */
+ /* The rest of this junk is to help gdb figure out what goes where */
+ u32 u_tsize; /* Text segment size (pages). */
+ u32 u_dsize; /* Data segment size (pages). */
+ u32 u_ssize; /* Stack segment size (pages). */
+ u32 start_code; /* Starting virtual address of text. */
+ u32 start_stack; /* Starting virtual address of stack area.
+ This is actually the bottom of the stack,
+ the top of the stack is always found in the
+ esp register. */
+ s32 signal; /* Signal that caused the core dump. */
+ u32 u_ar0; /* Used by gdb to help find the values for */
+ /* the registers. */
+ u32 magic; /* To uniquely identify a core file */
+ char u_comm[32]; /* User command that was responsible */
+};
+
+
+#define PT32_PSWMASK 0x0
+#define PT32_PSWADDR 0x04
+#define PT32_GPR0 0x08
+#define PT32_GPR15 0x44
+#define PT32_ACR0 0x48
+#define PT32_ACR15 0x84
+#define PT32_ORIGGPR2 0x88
+#define PT32_FPC 0x90
+#define PT32_FPR0_HI 0x98
+#define PT32_FPR15_LO 0x114
+#define PT32_CR_9 0x118
+#define PT32_CR_11 0x120
+#define PT32_IEEE_IP 0x13C
+#define PT32_LASTOFF PT32_IEEE_IP
+#define PT32_ENDREGS 0x140-1
+#define U32OFFSETOF(member) offsetof(struct user32,regs.member)
+#define U64OFFSETOF(member) offsetof(struct user,regs.member)
+#define U6432DIFF(member) (U64OFFSETOF(member) - U32OFFSETOF(member))
+#define PT_SINGLE_STEP (PT_CR_11+8)
+#define PT32_SINGLE_STEP (PT32_CR_11+4)
+
+#endif /* CONFIG_S390_SUPPORT */
+
int copy_user(struct task_struct *task,saddr_t useraddr, addr_t copyaddr,
int len, int tofromuser, int writingtouser)
{
int copylen=0,copymax;
addr_t realuseraddr;
saddr_t enduseraddr;
-
unsigned long mask;
-
#ifdef CONFIG_S390_SUPPORT
- if (current->thread.flags & S390_FLAG_31BIT) {
- /* adjust user offsets to 64 bit structure */
- if (useraddr < PT_PSWADDR / 2)
- useraddr = 2 * useraddr;
- else if(useraddr < PT_ACR0 / 2)
- useraddr = 2 * useraddr + sizeof(addr_t) / 2;
- else if(useraddr < PT_ACR0 / 2 + (PT_ORIGGPR2 - PT_ACR0))
- useraddr = useraddr + PT_ACR0 / 2;
- else if(useraddr < PT_ACR0 / 2 + (sizeof(struct user_regs_struct) - sizeof(addr_t) / 2 - PT_ACR0))
- useraddr = useraddr + PT_ACR0 / 2 + sizeof(addr_t) / 2;
- }
-#endif
-
+ int parent_31bit=current->thread.flags & S390_FLAG_31BIT;
+ int skip;
+#endif
enduseraddr=useraddr+len;
-
- if (useraddr < 0 || enduseraddr > sizeof(struct user)||
- (useraddr < PT_ENDREGS && (useraddr&3))||
- (enduseraddr < PT_ENDREGS && (enduseraddr&3)))
+ if ((useraddr<0||useraddr&3||enduseraddr&3)||
+#ifdef CONFIG_S390_SUPPORT
+ (parent_31bit && enduseraddr > sizeof(struct user32)) ||
+#endif
+ enduseraddr > sizeof(struct user))
return (-EIO);
+
+#ifdef CONFIG_S390_SUPPORT
+ if(parent_31bit)
+ {
+ if(useraddr != PT32_PSWMASK)
+ {
+ if (useraddr == PT32_PSWADDR)
+ useraddr = PT_PSWADDR+4;
+ else if(useraddr <= PT32_GPR15)
+ useraddr = ((useraddr-PT32_GPR0)*2) + PT_GPR0+4;
+ else if(useraddr <= PT32_ACR15)
+ useraddr += PT_ACR0-PT32_ACR0;
+ else if(useraddr == PT32_ORIGGPR2)
+ useraddr = PT_ORIGGPR2+4;
+ else if(useraddr <= PT32_FPR15_LO)
+ useraddr += PT_FPR0-PT32_FPR0_HI;
+ else if(useraddr <= PT32_CR_11)
+ useraddr = ((useraddr-PT32_CR_9)*2) + PT_CR_9+4;
+ else if(useraddr == PT32_SINGLE_STEP)
+ useraddr = PT_SINGLE_STEP;
+ else if(useraddr <= U32OFFSETOF(per_info.ending_addr))
+ useraddr = (((useraddr-U32OFFSETOF(per_info.starting_addr)))*2) +
+ U64OFFSETOF(per_info.starting_addr)+4;
+ else if( useraddr == U32OFFSETOF(per_info.lowcore.words.perc_atmid))
+ useraddr = U64OFFSETOF(per_info.lowcore.words.perc_atmid);
+ else if( useraddr == U32OFFSETOF(per_info.lowcore.words.address))
+ useraddr = U64OFFSETOF(per_info.lowcore.words.address)+4;
+ else if(useraddr == U32OFFSETOF(per_info.lowcore.words.access_id))
+ useraddr = U64OFFSETOF(per_info.lowcore.words.access_id);
+ else if(useraddr == PT32_IEEE_IP)
+ useraddr = PT_IEEE_IP+4;
+ }
+ }
+#endif /* CONFIG_S390_SUPPORT */
+
while(len>0)
{
+#ifdef CONFIG_S390_SUPPORT
+ skip=0;
+#endif
mask=PSW_ADDR_MASK;
if(useraddr<PT_FPC)
{
realuseraddr=((addr_t) __KSTK_PTREGS(task)) + useraddr;
- if(useraddr<PT_PSWMASK)
- {
- copymax=PT_PSWMASK;
- }
- else if(useraddr<(PT_PSWMASK+8))
+ if(useraddr<(PT_PSWMASK+8))
{
- copymax=(PT_PSWMASK+8);
+ if(parent_31bit)
+ {
+ copymax=PT_PSWMASK+4;
+#ifdef CONFIG_S390_SUPPORT
+ skip=8;
+#endif
+ }
+ else
+ {
+ copymax=PT_PSWMASK+8;
+ }
if(writingtouser)
mask=PSW_MASK_DEBUGCHANGE;
}
@@ -184,10 +322,25 @@
{
copymax=PT_PSWADDR+8;
mask=PSW_ADDR_DEBUGCHANGE;
+#ifdef CONFIG_S390_SUPPORT
+ if(parent_31bit)
+ skip=4;
+#endif
+
}
else
- copymax=PT_FPC;
-
+ {
+#ifdef CONFIG_S390_SUPPORT
+ if(parent_31bit && useraddr <= PT_GPR15+4)
+ {
+ copymax=useraddr+4;
+ if(useraddr<PT_GPR15+4)
+ skip=4;
+ }
+ else
+#endif
+ copymax=PT_FPC;
+ }
}
else if(useraddr<(PT_FPR15+sizeof(freg_t)))
{
@@ -196,7 +349,33 @@
}
else if(useraddr<sizeof(struct user_regs_struct))
{
- copymax=sizeof(struct user_regs_struct);
+#ifdef CONFIG_S390_SUPPORT
+ if( parent_31bit && useraddr <= PT_IEEE_IP+4)
+ {
+ switch(useraddr)
+ {
+ case PT_CR_11+4:
+ case U64OFFSETOF(per_info.ending_addr)+4:
+ case U64OFFSETOF(per_info.lowcore.words.address)+4:
+ copymax=useraddr+4;
+ break;
+ case PT_SINGLE_STEP:
+ case U64OFFSETOF(per_info.lowcore.words.perc_atmid):
+ /* We copy 2 bytes in excess for the atmid member this also gets around */
+ /* alignment for this member in 32 bit */
+ skip=8;
+ copymax=useraddr+4;
+ break;
+ default:
+ copymax=useraddr+4;
+ skip=4;
+ }
+ }
+ else
+#endif
+ {
+ copymax=sizeof(struct user_regs_struct);
+ }
realuseraddr=(addr_t)&(((u8 *)&task->thread.per_info)[useraddr-PT_CR_9]);
}
else
@@ -210,7 +389,11 @@
return (-EIO);
copyaddr+=copylen;
len-=copylen;
- useraddr+=copylen;
+ useraddr+=copylen
+#if CONFIG_S390_SUPPORT
+ +skip
+#endif
+ ;
}
FixPerRegisters(task);
return(0);
@@ -227,150 +410,6 @@
clear_single_step(child);
}
-asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
-{
- struct task_struct *child;
- int ret = -EPERM;
- unsigned long flags;
- unsigned long tmp;
- int copied;
- ptrace_area parea;
-
- lock_kernel();
- if (request == PTRACE_TRACEME)
- {
- /* are we already being traced? */
- if (current->ptrace & PT_PTRACED)
- goto out;
- /* set the ptrace bit in the process flags. */
- current->ptrace |= PT_PTRACED;
- ret = 0;
- goto out;
- }
- ret = -ESRCH;
- read_lock(&tasklist_lock);
- child = find_task_by_pid(pid);
- read_unlock(&tasklist_lock);
- if (!child)
- goto out;
- ret = -EPERM;
- if (pid == 1) /* you may not mess with init */
- goto out;
- if (request == PTRACE_ATTACH)
- {
- ret = ptrace_attach(child);
- goto out;
- }
- ret = -ESRCH;
- // printk("child=%lX child->flags=%lX",child,child->flags);
- /* I added child!=current line so we can get the */
- /* ieee_instruction_pointer from the user structure DJB */
- if(child!=current)
- {
- if (!(child->ptrace & PT_PTRACED))
- goto out;
- if (child->state != TASK_STOPPED)
- {
- if (request != PTRACE_KILL)
- goto out;
- }
- if (child->p_pptr != current)
- goto out;
- }
- switch (request)
- {
- /* If I and D space are separate, these will need to be fixed. */
- case PTRACE_PEEKTEXT: /* read word at location addr. */
- case PTRACE_PEEKDATA:
- copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
- ret = -EIO;
- if (copied != sizeof(tmp))
- goto out;
- ret = put_user(tmp,(unsigned long *) data);
- goto out;
-
- /* read the word at location addr in the USER area. */
- case PTRACE_PEEKUSR:
- ret=copy_user(child,addr,data,sizeof(unsigned long),1,0);
- break;
-
- /* If I and D space are separate, this will have to be fixed. */
- case PTRACE_POKETEXT: /* write the word at location addr. */
- case PTRACE_POKEDATA:
- ret = 0;
- if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
- goto out;
- ret = -EIO;
- goto out;
- break;
-
- case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
- ret=copy_user(child,addr,(addr_t)&data,sizeof(unsigned long),0,1);
- break;
-
- case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
- case PTRACE_CONT: /* restart after signal. */
- ret = -EIO;
- if ((unsigned long) data >= _NSIG)
- break;
- if (request == PTRACE_SYSCALL)
- child->ptrace |= PT_TRACESYS;
- else
- child->ptrace &= ~PT_TRACESYS;
- child->exit_code = data;
- /* make sure the single step bit is not set. */
- clear_single_step(child);
- wake_up_process(child);
- ret = 0;
- break;
-
-/*
- * make the child exit. Best I can do is send it a sigkill.
- * perhaps it should be put in the status that it wants to
- * exit.
- */
- case PTRACE_KILL:
- ret = 0;
- if (child->state == TASK_ZOMBIE) /* already dead */
- break;
- child->exit_code = SIGKILL;
- clear_single_step(child);
- wake_up_process(child);
- /* make sure the single step bit is not set. */
- break;
-
- case PTRACE_SINGLESTEP: /* set the trap flag. */
- ret = -EIO;
- if ((unsigned long) data >= _NSIG)
- break;
- child->ptrace &= ~PT_TRACESYS;
- child->exit_code = data;
- set_single_step(child);
- /* give it a chance to run. */
- wake_up_process(child);
- ret = 0;
- break;
-
- case PTRACE_DETACH: /* detach a process that was attached. */
- ret = ptrace_detach(child, data);
- break;
-
- case PTRACE_PEEKUSR_AREA:
- case PTRACE_POKEUSR_AREA:
- if(copy_from_user(&parea,(void *)addr,sizeof(parea)) == 0)
- ret=copy_user(child,parea.kernel_addr,parea.process_addr,
- parea.len,1,(request==PTRACE_POKEUSR_AREA));
- else ret = -EFAULT;
- break;
- default:
- ret = -EIO;
- break;
- }
- out:
- unlock_kernel();
- return ret;
-}
-
typedef struct
{
__u32 len;
@@ -378,15 +417,20 @@
__u32 process_addr;
} ptrace_area_emu31;
-asmlinkage int sys32_ptrace(long request, long pid, long addr, s32 data)
+
+asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
{
struct task_struct *child;
int ret = -EPERM;
- unsigned long flags;
- u32 tmp;
int copied;
- ptrace_area parea;
-
+#ifdef CONFIG_S390_SUPPORT
+ int parent_31bit;
+ int sizeof_parent_long;
+ u8 *dataptr;
+#else
+#define sizeof_parent_long 8
+#define dataptr (u8 *)&data
+#endif
lock_kernel();
if (request == PTRACE_TRACEME)
{
@@ -401,16 +445,18 @@
ret = -ESRCH;
read_lock(&tasklist_lock);
child = find_task_by_pid(pid);
+ if (child)
+ get_task_struct(child);
read_unlock(&tasklist_lock);
if (!child)
goto out;
ret = -EPERM;
if (pid == 1) /* you may not mess with init */
- goto out;
+ goto out_tsk;
if (request == PTRACE_ATTACH)
{
ret = ptrace_attach(child);
- goto out;
+ goto out_tsk;
}
ret = -ESRCH;
// printk("child=%lX child->flags=%lX",child,child->flags);
@@ -419,44 +465,52 @@
if(child!=current)
{
if (!(child->ptrace & PT_PTRACED))
- goto out;
+ goto out_tsk;
if (child->state != TASK_STOPPED)
{
if (request != PTRACE_KILL)
- goto out;
+ goto out_tsk;
}
if (child->p_pptr != current)
- goto out;
+ goto out_tsk;
}
+#ifdef CONFIG_S390_SUPPORT
+ parent_31bit=(current->thread.flags & S390_FLAG_31BIT);
+ sizeof_parent_long=(parent_31bit ? 4:8);
+ dataptr=&(((u8 *)&data)[parent_31bit ? 4:0]);
+#endif
switch (request)
{
/* If I and D space are separate, these will need to be fixed. */
case PTRACE_PEEKTEXT: /* read word at location addr. */
case PTRACE_PEEKDATA:
- copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+ {
+ u8 tmp[8];
+ copied = access_process_vm(child, addr, tmp, sizeof_parent_long, 0);
ret = -EIO;
- if (copied != sizeof(tmp))
- goto out;
- ret = put_user(tmp,(u32 *)(unsigned long)data);
- goto out;
-
+ if (copied != sizeof_parent_long)
+ break;
+ ret = copy_to_user((void *)data,tmp,sizeof_parent_long);
+ ret = ret ? -EFAULT : 0;
+ break;
+
+ }
/* read the word at location addr in the USER area. */
case PTRACE_PEEKUSR:
- ret=copy_user(child,addr,data,sizeof(u32),1,0);
+ ret=copy_user(child,addr,data,sizeof_parent_long,1,0);
break;
/* If I and D space are separate, this will have to be fixed. */
case PTRACE_POKETEXT: /* write the word at location addr. */
case PTRACE_POKEDATA:
ret = 0;
- if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
- goto out;
+ if (access_process_vm(child, addr,dataptr, sizeof_parent_long, 1) == sizeof_parent_long)
+ break;
ret = -EIO;
- goto out;
break;
case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
- ret=copy_user(child,addr,(addr_t)&data,sizeof(u32),0,1);
+ ret=copy_user(child,addr,(addr_t)dataptr,sizeof_parent_long,0,1);
break;
case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
@@ -503,44 +557,41 @@
break;
case PTRACE_DETACH: /* detach a process that was attached. */
- ret = -EIO;
- if ((unsigned long) data >= _NSIG)
- break;
- child->ptrace &= ~(PT_PTRACED|PT_TRACESYS);
- child->exit_code = data;
- write_lock_irqsave(&tasklist_lock, flags);
- REMOVE_LINKS(child);
- child->p_pptr = child->p_opptr;
- SET_LINKS(child);
- write_unlock_irqrestore(&tasklist_lock, flags);
- /* make sure the single step bit is not set. */
- clear_single_step(child);
- wake_up_process(child);
- ret = 0;
+ ret = ptrace_detach(child, data);
break;
+
case PTRACE_PEEKUSR_AREA:
case PTRACE_POKEUSR_AREA:
+ if(parent_31bit)
{
- ptrace_area_emu31 * parea31 = (void *)addr;
- if (!access_ok(VERIFY_READ, parea31, sizeof(*parea31)))
- return(-EFAULT);
- ret = __get_user(parea.len, &parea31->len);
- ret |= __get_user(parea.kernel_addr, &parea31->kernel_addr);
- ret |= __get_user(parea.process_addr, &parea31->process_addr);
- if(ret==0)
- ret=copy_user(child,parea.kernel_addr,parea.process_addr,
- parea.len,1,(request==PTRACE_POKEUSR_AREA));
- break;
+ ptrace_area_emu31 parea;
+ if(copy_from_user(&parea,(void *)addr,sizeof(parea))==0)
+ ret=copy_user(child,parea.kernel_addr,parea.process_addr,
+ parea.len,1,(request==PTRACE_POKEUSR_AREA));
+ else ret = -EFAULT;
}
+ else
+ {
+ ptrace_area parea;
+ if(copy_from_user(&parea,(void *)addr,sizeof(parea))==0)
+ ret=copy_user(child,parea.kernel_addr,parea.process_addr,
+ parea.len,1,(request==PTRACE_POKEUSR_AREA));
+ else ret = -EFAULT;
+ }
+ break;
default:
ret = -EIO;
break;
}
+ out_tsk:
+ free_task_struct(child);
out:
unlock_kernel();
return ret;
}
+
+
asmlinkage void syscall_trace(void)
{
lock_kernel();
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)