diff options
author | Roland McGrath <roland@redhat.com> | 2008-01-30 07:31:01 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-01-30 07:31:01 -0500 |
commit | 06ee1b687ac91698ccd47fa652d5b3cf1bfcd806 (patch) | |
tree | db41e43959a366a5bfa31f23d631402dd2b78298 /arch/x86/kernel/ptrace.c | |
parent | e39c2891415b3b5c7381ece06bb45b3c7bdd4342 (diff) |
x86: x86 ptrace getreg/putreg cleanup
This cleans up the getreg/putreg functions to move the special cases
(segment registers and eflags) out into their own subroutines.
Signed-off-by: Roland McGrath <roland@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch/x86/kernel/ptrace.c')
-rw-r--r-- | arch/x86/kernel/ptrace.c | 162 |
1 files changed, 96 insertions, 66 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index b71226d653ed..eaec75a4094b 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c | |||
@@ -45,92 +45,122 @@ | |||
45 | static long *pt_regs_access(struct pt_regs *regs, unsigned long regno) | 45 | static long *pt_regs_access(struct pt_regs *regs, unsigned long regno) |
46 | { | 46 | { |
47 | BUILD_BUG_ON(offsetof(struct pt_regs, bx) != 0); | 47 | BUILD_BUG_ON(offsetof(struct pt_regs, bx) != 0); |
48 | regno >>= 2; | ||
48 | if (regno > FS) | 49 | if (regno > FS) |
49 | --regno; | 50 | --regno; |
50 | return ®s->bx + regno; | 51 | return ®s->bx + regno; |
51 | } | 52 | } |
52 | 53 | ||
53 | static int putreg(struct task_struct *child, | 54 | static u16 get_segment_reg(struct task_struct *task, unsigned long offset) |
54 | unsigned long regno, unsigned long value) | ||
55 | { | 55 | { |
56 | struct pt_regs *regs = task_pt_regs(child); | 56 | /* |
57 | regno >>= 2; | 57 | * Returning the value truncates it to 16 bits. |
58 | switch (regno) { | 58 | */ |
59 | case GS: | 59 | unsigned int retval; |
60 | if (value && (value & 3) != 3) | 60 | if (offset != offsetof(struct user_regs_struct, gs)) |
61 | return -EIO; | 61 | retval = *pt_regs_access(task_pt_regs(task), offset); |
62 | child->thread.gs = value; | 62 | else { |
63 | if (child == current) | 63 | retval = task->thread.gs; |
64 | if (task == current) | ||
65 | savesegment(gs, retval); | ||
66 | } | ||
67 | return retval; | ||
68 | } | ||
69 | |||
70 | static int set_segment_reg(struct task_struct *task, | ||
71 | unsigned long offset, u16 value) | ||
72 | { | ||
73 | /* | ||
74 | * The value argument was already truncated to 16 bits. | ||
75 | */ | ||
76 | if (value && (value & 3) != 3) | ||
77 | return -EIO; | ||
78 | |||
79 | if (offset != offsetof(struct user_regs_struct, gs)) | ||
80 | *pt_regs_access(task_pt_regs(task), offset) = value; | ||
81 | else { | ||
82 | task->thread.gs = value; | ||
83 | if (task == current) | ||
64 | /* | 84 | /* |
65 | * The user-mode %gs is not affected by | 85 | * The user-mode %gs is not affected by |
66 | * kernel entry, so we must update the CPU. | 86 | * kernel entry, so we must update the CPU. |
67 | */ | 87 | */ |
68 | loadsegment(gs, value); | 88 | loadsegment(gs, value); |
69 | return 0; | ||
70 | case DS: | ||
71 | case ES: | ||
72 | case FS: | ||
73 | if (value && (value & 3) != 3) | ||
74 | return -EIO; | ||
75 | value &= 0xffff; | ||
76 | break; | ||
77 | case SS: | ||
78 | case CS: | ||
79 | if ((value & 3) != 3) | ||
80 | return -EIO; | ||
81 | value &= 0xffff; | ||
82 | break; | ||
83 | case EFL: | ||
84 | value &= FLAG_MASK; | ||
85 | /* | ||
86 | * If the user value contains TF, mark that | ||
87 | * it was not "us" (the debugger) that set it. | ||
88 | * If not, make sure it stays set if we had. | ||
89 | */ | ||
90 | if (value & X86_EFLAGS_TF) | ||
91 | clear_tsk_thread_flag(child, TIF_FORCED_TF); | ||
92 | else if (test_tsk_thread_flag(child, TIF_FORCED_TF)) | ||
93 | value |= X86_EFLAGS_TF; | ||
94 | value |= regs->flags & ~FLAG_MASK; | ||
95 | break; | ||
96 | } | 89 | } |
97 | *pt_regs_access(regs, regno) = value; | 90 | |
98 | return 0; | 91 | return 0; |
99 | } | 92 | } |
100 | 93 | ||
101 | static unsigned long getreg(struct task_struct *child, unsigned long regno) | 94 | static unsigned long get_flags(struct task_struct *task) |
102 | { | 95 | { |
103 | struct pt_regs *regs = task_pt_regs(child); | 96 | unsigned long retval = task_pt_regs(task)->flags; |
104 | unsigned long retval = ~0UL; | 97 | |
98 | /* | ||
99 | * If the debugger set TF, hide it from the readout. | ||
100 | */ | ||
101 | if (test_tsk_thread_flag(task, TIF_FORCED_TF)) | ||
102 | retval &= ~X86_EFLAGS_TF; | ||
105 | 103 | ||
106 | regno >>= 2; | ||
107 | switch (regno) { | ||
108 | case EFL: | ||
109 | /* | ||
110 | * If the debugger set TF, hide it from the readout. | ||
111 | */ | ||
112 | retval = regs->flags; | ||
113 | if (test_tsk_thread_flag(child, TIF_FORCED_TF)) | ||
114 | retval &= ~X86_EFLAGS_TF; | ||
115 | break; | ||
116 | case GS: | ||
117 | retval = child->thread.gs; | ||
118 | if (child == current) | ||
119 | savesegment(gs, retval); | ||
120 | break; | ||
121 | case DS: | ||
122 | case ES: | ||
123 | case FS: | ||
124 | case SS: | ||
125 | case CS: | ||
126 | retval = 0xffff; | ||
127 | /* fall through */ | ||
128 | default: | ||
129 | retval &= *pt_regs_access(regs, regno); | ||
130 | } | ||
131 | return retval; | 104 | return retval; |
132 | } | 105 | } |
133 | 106 | ||
107 | static int set_flags(struct task_struct *task, unsigned long value) | ||
108 | { | ||
109 | struct pt_regs *regs = task_pt_regs(task); | ||
110 | |||
111 | /* | ||
112 | * If the user value contains TF, mark that | ||
113 | * it was not "us" (the debugger) that set it. | ||
114 | * If not, make sure it stays set if we had. | ||
115 | */ | ||
116 | if (value & X86_EFLAGS_TF) | ||
117 | clear_tsk_thread_flag(task, TIF_FORCED_TF); | ||
118 | else if (test_tsk_thread_flag(task, TIF_FORCED_TF)) | ||
119 | value |= X86_EFLAGS_TF; | ||
120 | |||
121 | regs->flags = (regs->flags & ~FLAG_MASK) | (value & FLAG_MASK); | ||
122 | |||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | static int putreg(struct task_struct *child, | ||
127 | unsigned long offset, unsigned long value) | ||
128 | { | ||
129 | switch (offset) { | ||
130 | case offsetof(struct user_regs_struct, cs): | ||
131 | case offsetof(struct user_regs_struct, ds): | ||
132 | case offsetof(struct user_regs_struct, es): | ||
133 | case offsetof(struct user_regs_struct, fs): | ||
134 | case offsetof(struct user_regs_struct, gs): | ||
135 | case offsetof(struct user_regs_struct, ss): | ||
136 | return set_segment_reg(child, offset, value); | ||
137 | |||
138 | case offsetof(struct user_regs_struct, flags): | ||
139 | return set_flags(child, value); | ||
140 | } | ||
141 | |||
142 | *pt_regs_access(task_pt_regs(child), offset) = value; | ||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | static unsigned long getreg(struct task_struct *task, unsigned long offset) | ||
147 | { | ||
148 | switch (offset) { | ||
149 | case offsetof(struct user_regs_struct, cs): | ||
150 | case offsetof(struct user_regs_struct, ds): | ||
151 | case offsetof(struct user_regs_struct, es): | ||
152 | case offsetof(struct user_regs_struct, fs): | ||
153 | case offsetof(struct user_regs_struct, gs): | ||
154 | case offsetof(struct user_regs_struct, ss): | ||
155 | return get_segment_reg(task, offset); | ||
156 | |||
157 | case offsetof(struct user_regs_struct, flags): | ||
158 | return get_flags(task); | ||
159 | } | ||
160 | |||
161 | return *pt_regs_access(task_pt_regs(task), offset); | ||
162 | } | ||
163 | |||
134 | /* | 164 | /* |
135 | * This function is trivial and will be inlined by the compiler. | 165 | * This function is trivial and will be inlined by the compiler. |
136 | * Having it separates the implementation details of debug | 166 | * Having it separates the implementation details of debug |