diff options
Diffstat (limited to 'arch/tile/kernel/ptrace.c')
-rw-r--r-- | arch/tile/kernel/ptrace.c | 140 |
1 files changed, 113 insertions, 27 deletions
diff --git a/arch/tile/kernel/ptrace.c b/arch/tile/kernel/ptrace.c index e92e40527d6d..9835312d5a91 100644 --- a/arch/tile/kernel/ptrace.c +++ b/arch/tile/kernel/ptrace.c | |||
@@ -19,7 +19,10 @@ | |||
19 | #include <linux/kprobes.h> | 19 | #include <linux/kprobes.h> |
20 | #include <linux/compat.h> | 20 | #include <linux/compat.h> |
21 | #include <linux/uaccess.h> | 21 | #include <linux/uaccess.h> |
22 | #include <linux/regset.h> | ||
23 | #include <linux/elf.h> | ||
22 | #include <asm/traps.h> | 24 | #include <asm/traps.h> |
25 | #include <arch/chip.h> | ||
23 | 26 | ||
24 | void user_enable_single_step(struct task_struct *child) | 27 | void user_enable_single_step(struct task_struct *child) |
25 | { | 28 | { |
@@ -45,6 +48,100 @@ void ptrace_disable(struct task_struct *child) | |||
45 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | 48 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); |
46 | } | 49 | } |
47 | 50 | ||
51 | /* | ||
52 | * Get registers from task and ready the result for userspace. | ||
53 | * Note that we localize the API issues to getregs() and putregs() at | ||
54 | * some cost in performance, e.g. we need a full pt_regs copy for | ||
55 | * PEEKUSR, and two copies for POKEUSR. But in general we expect | ||
56 | * GETREGS/PUTREGS to be the API of choice anyway. | ||
57 | */ | ||
58 | static char *getregs(struct task_struct *child, struct pt_regs *uregs) | ||
59 | { | ||
60 | *uregs = *task_pt_regs(child); | ||
61 | |||
62 | /* Set up flags ABI bits. */ | ||
63 | uregs->flags = 0; | ||
64 | #ifdef CONFIG_COMPAT | ||
65 | if (task_thread_info(child)->status & TS_COMPAT) | ||
66 | uregs->flags |= PT_FLAGS_COMPAT; | ||
67 | #endif | ||
68 | |||
69 | return (char *)uregs; | ||
70 | } | ||
71 | |||
72 | /* Put registers back to task. */ | ||
73 | static void putregs(struct task_struct *child, struct pt_regs *uregs) | ||
74 | { | ||
75 | struct pt_regs *regs = task_pt_regs(child); | ||
76 | |||
77 | /* Don't allow overwriting the kernel-internal flags word. */ | ||
78 | uregs->flags = regs->flags; | ||
79 | |||
80 | /* Only allow setting the ICS bit in the ex1 word. */ | ||
81 | uregs->ex1 = PL_ICS_EX1(USER_PL, EX1_ICS(uregs->ex1)); | ||
82 | |||
83 | *regs = *uregs; | ||
84 | } | ||
85 | |||
86 | enum tile_regset { | ||
87 | REGSET_GPR, | ||
88 | }; | ||
89 | |||
90 | static int tile_gpr_get(struct task_struct *target, | ||
91 | const struct user_regset *regset, | ||
92 | unsigned int pos, unsigned int count, | ||
93 | void *kbuf, void __user *ubuf) | ||
94 | { | ||
95 | struct pt_regs regs; | ||
96 | |||
97 | getregs(target, ®s); | ||
98 | |||
99 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, ®s, 0, | ||
100 | sizeof(regs)); | ||
101 | } | ||
102 | |||
103 | static int tile_gpr_set(struct task_struct *target, | ||
104 | const struct user_regset *regset, | ||
105 | unsigned int pos, unsigned int count, | ||
106 | const void *kbuf, const void __user *ubuf) | ||
107 | { | ||
108 | int ret; | ||
109 | struct pt_regs regs; | ||
110 | |||
111 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®s, 0, | ||
112 | sizeof(regs)); | ||
113 | if (ret) | ||
114 | return ret; | ||
115 | |||
116 | putregs(target, ®s); | ||
117 | |||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | static const struct user_regset tile_user_regset[] = { | ||
122 | [REGSET_GPR] = { | ||
123 | .core_note_type = NT_PRSTATUS, | ||
124 | .n = ELF_NGREG, | ||
125 | .size = sizeof(elf_greg_t), | ||
126 | .align = sizeof(elf_greg_t), | ||
127 | .get = tile_gpr_get, | ||
128 | .set = tile_gpr_set, | ||
129 | }, | ||
130 | }; | ||
131 | |||
132 | static const struct user_regset_view tile_user_regset_view = { | ||
133 | .name = CHIP_ARCH_NAME, | ||
134 | .e_machine = ELF_ARCH, | ||
135 | .ei_osabi = ELF_OSABI, | ||
136 | .regsets = tile_user_regset, | ||
137 | .n = ARRAY_SIZE(tile_user_regset), | ||
138 | }; | ||
139 | |||
140 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | ||
141 | { | ||
142 | return &tile_user_regset_view; | ||
143 | } | ||
144 | |||
48 | long arch_ptrace(struct task_struct *child, long request, | 145 | long arch_ptrace(struct task_struct *child, long request, |
49 | unsigned long addr, unsigned long data) | 146 | unsigned long addr, unsigned long data) |
50 | { | 147 | { |
@@ -53,14 +150,13 @@ long arch_ptrace(struct task_struct *child, long request, | |||
53 | long ret = -EIO; | 150 | long ret = -EIO; |
54 | char *childreg; | 151 | char *childreg; |
55 | struct pt_regs copyregs; | 152 | struct pt_regs copyregs; |
56 | int ex1_offset; | ||
57 | 153 | ||
58 | switch (request) { | 154 | switch (request) { |
59 | 155 | ||
60 | case PTRACE_PEEKUSR: /* Read register from pt_regs. */ | 156 | case PTRACE_PEEKUSR: /* Read register from pt_regs. */ |
61 | if (addr >= PTREGS_SIZE) | 157 | if (addr >= PTREGS_SIZE) |
62 | break; | 158 | break; |
63 | childreg = (char *)task_pt_regs(child) + addr; | 159 | childreg = getregs(child, ©regs) + addr; |
64 | #ifdef CONFIG_COMPAT | 160 | #ifdef CONFIG_COMPAT |
65 | if (is_compat_task()) { | 161 | if (is_compat_task()) { |
66 | if (addr & (sizeof(compat_long_t)-1)) | 162 | if (addr & (sizeof(compat_long_t)-1)) |
@@ -79,17 +175,7 @@ long arch_ptrace(struct task_struct *child, long request, | |||
79 | case PTRACE_POKEUSR: /* Write register in pt_regs. */ | 175 | case PTRACE_POKEUSR: /* Write register in pt_regs. */ |
80 | if (addr >= PTREGS_SIZE) | 176 | if (addr >= PTREGS_SIZE) |
81 | break; | 177 | break; |
82 | childreg = (char *)task_pt_regs(child) + addr; | 178 | childreg = getregs(child, ©regs) + addr; |
83 | |||
84 | /* Guard against overwrites of the privilege level. */ | ||
85 | ex1_offset = PTREGS_OFFSET_EX1; | ||
86 | #if defined(CONFIG_COMPAT) && defined(__BIG_ENDIAN) | ||
87 | if (is_compat_task()) /* point at low word */ | ||
88 | ex1_offset += sizeof(compat_long_t); | ||
89 | #endif | ||
90 | if (addr == ex1_offset) | ||
91 | data = PL_ICS_EX1(USER_PL, EX1_ICS(data)); | ||
92 | |||
93 | #ifdef CONFIG_COMPAT | 179 | #ifdef CONFIG_COMPAT |
94 | if (is_compat_task()) { | 180 | if (is_compat_task()) { |
95 | if (addr & (sizeof(compat_long_t)-1)) | 181 | if (addr & (sizeof(compat_long_t)-1)) |
@@ -102,24 +188,20 @@ long arch_ptrace(struct task_struct *child, long request, | |||
102 | break; | 188 | break; |
103 | *(long *)childreg = data; | 189 | *(long *)childreg = data; |
104 | } | 190 | } |
191 | putregs(child, ©regs); | ||
105 | ret = 0; | 192 | ret = 0; |
106 | break; | 193 | break; |
107 | 194 | ||
108 | case PTRACE_GETREGS: /* Get all registers from the child. */ | 195 | case PTRACE_GETREGS: /* Get all registers from the child. */ |
109 | if (copy_to_user(datap, task_pt_regs(child), | 196 | ret = copy_regset_to_user(child, &tile_user_regset_view, |
110 | sizeof(struct pt_regs)) == 0) { | 197 | REGSET_GPR, 0, |
111 | ret = 0; | 198 | sizeof(struct pt_regs), datap); |
112 | } | ||
113 | break; | 199 | break; |
114 | 200 | ||
115 | case PTRACE_SETREGS: /* Set all registers in the child. */ | 201 | case PTRACE_SETREGS: /* Set all registers in the child. */ |
116 | if (copy_from_user(©regs, datap, | 202 | ret = copy_regset_from_user(child, &tile_user_regset_view, |
117 | sizeof(struct pt_regs)) == 0) { | 203 | REGSET_GPR, 0, |
118 | copyregs.ex1 = | 204 | sizeof(struct pt_regs), datap); |
119 | PL_ICS_EX1(USER_PL, EX1_ICS(copyregs.ex1)); | ||
120 | *task_pt_regs(child) = copyregs; | ||
121 | ret = 0; | ||
122 | } | ||
123 | break; | 205 | break; |
124 | 206 | ||
125 | case PTRACE_GETFPREGS: /* Get the child FPU state. */ | 207 | case PTRACE_GETFPREGS: /* Get the child FPU state. */ |
@@ -128,12 +210,16 @@ long arch_ptrace(struct task_struct *child, long request, | |||
128 | 210 | ||
129 | case PTRACE_SETOPTIONS: | 211 | case PTRACE_SETOPTIONS: |
130 | /* Support TILE-specific ptrace options. */ | 212 | /* Support TILE-specific ptrace options. */ |
131 | child->ptrace &= ~PT_TRACE_MASK_TILE; | 213 | BUILD_BUG_ON(PTRACE_O_MASK_TILE & PTRACE_O_MASK); |
132 | tmp = data & PTRACE_O_MASK_TILE; | 214 | tmp = data & PTRACE_O_MASK_TILE; |
133 | data &= ~PTRACE_O_MASK_TILE; | 215 | data &= ~PTRACE_O_MASK_TILE; |
134 | ret = ptrace_request(child, request, addr, data); | 216 | ret = ptrace_request(child, request, addr, data); |
135 | if (tmp & PTRACE_O_TRACEMIGRATE) | 217 | if (ret == 0) { |
136 | child->ptrace |= PT_TRACE_MIGRATE; | 218 | unsigned int flags = child->ptrace; |
219 | flags &= ~(PTRACE_O_MASK_TILE << PT_OPT_FLAG_SHIFT); | ||
220 | flags |= (tmp << PT_OPT_FLAG_SHIFT); | ||
221 | child->ptrace = flags; | ||
222 | } | ||
137 | break; | 223 | break; |
138 | 224 | ||
139 | default: | 225 | default: |