diff options
author | Ingo Molnar <mingo@elte.hu> | 2009-02-20 14:37:05 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-02-20 18:09:40 -0500 |
commit | 107a03678cac0dd6cf7095f81442a4fa477e4700 (patch) | |
tree | 5d7cb450dd33b2dd1ac3d712049250424c5bbfcd /arch | |
parent | 2d4a71676f4d89418a0d53e60b89e8b804b390b2 (diff) |
x86, mm: fault.c, refactor/simplify the is_prefetch() code
Impact: no functionality changed
Factor out the opcode checker into a helper inline.
The code got a tiny bit smaller:
text data bss dec hex filename
4632 32 24 4688 1250 fault.o.before
4618 32 24 4674 1242 fault.o.after
And it got cleaner / easier to review as well.
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/mm/fault.c | 100 |
1 files changed, 50 insertions, 50 deletions
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 351d679bf977..72973c7682ba 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c | |||
@@ -99,12 +99,58 @@ static inline int notify_page_fault(struct pt_regs *regs) | |||
99 | * | 99 | * |
100 | * Opcode checker based on code by Richard Brunner. | 100 | * Opcode checker based on code by Richard Brunner. |
101 | */ | 101 | */ |
102 | static inline int | ||
103 | check_prefetch_opcode(struct pt_regs *regs, unsigned char *instr, | ||
104 | unsigned char opcode, int *prefetch) | ||
105 | { | ||
106 | unsigned char instr_hi = opcode & 0xf0; | ||
107 | unsigned char instr_lo = opcode & 0x0f; | ||
108 | |||
109 | switch (instr_hi) { | ||
110 | case 0x20: | ||
111 | case 0x30: | ||
112 | /* | ||
113 | * Values 0x26,0x2E,0x36,0x3E are valid x86 prefixes. | ||
114 | * In X86_64 long mode, the CPU will signal invalid | ||
115 | * opcode if some of these prefixes are present so | ||
116 | * X86_64 will never get here anyway | ||
117 | */ | ||
118 | return ((instr_lo & 7) == 0x6); | ||
119 | #ifdef CONFIG_X86_64 | ||
120 | case 0x40: | ||
121 | /* | ||
122 | * In AMD64 long mode 0x40..0x4F are valid REX prefixes | ||
123 | * Need to figure out under what instruction mode the | ||
124 | * instruction was issued. Could check the LDT for lm, | ||
125 | * but for now it's good enough to assume that long | ||
126 | * mode only uses well known segments or kernel. | ||
127 | */ | ||
128 | return (!user_mode(regs)) || (regs->cs == __USER_CS); | ||
129 | #endif | ||
130 | case 0x60: | ||
131 | /* 0x64 thru 0x67 are valid prefixes in all modes. */ | ||
132 | return (instr_lo & 0xC) == 0x4; | ||
133 | case 0xF0: | ||
134 | /* 0xF0, 0xF2, 0xF3 are valid prefixes in all modes. */ | ||
135 | return !instr_lo || (instr_lo>>1) == 1; | ||
136 | case 0x00: | ||
137 | /* Prefetch instruction is 0x0F0D or 0x0F18 */ | ||
138 | if (probe_kernel_address(instr, opcode)) | ||
139 | return 0; | ||
140 | |||
141 | *prefetch = (instr_lo == 0xF) && | ||
142 | (opcode == 0x0D || opcode == 0x18); | ||
143 | return 0; | ||
144 | default: | ||
145 | return 0; | ||
146 | } | ||
147 | } | ||
148 | |||
102 | static int | 149 | static int |
103 | is_prefetch(struct pt_regs *regs, unsigned long error_code, unsigned long addr) | 150 | is_prefetch(struct pt_regs *regs, unsigned long error_code, unsigned long addr) |
104 | { | 151 | { |
105 | unsigned char *max_instr; | 152 | unsigned char *max_instr; |
106 | unsigned char *instr; | 153 | unsigned char *instr; |
107 | int scan_more = 1; | ||
108 | int prefetch = 0; | 154 | int prefetch = 0; |
109 | 155 | ||
110 | /* | 156 | /* |
@@ -114,68 +160,22 @@ is_prefetch(struct pt_regs *regs, unsigned long error_code, unsigned long addr) | |||
114 | if (error_code & PF_INSTR) | 160 | if (error_code & PF_INSTR) |
115 | return 0; | 161 | return 0; |
116 | 162 | ||
117 | instr = (unsigned char *)convert_ip_to_linear(current, regs); | 163 | instr = (void *)convert_ip_to_linear(current, regs); |
118 | max_instr = instr + 15; | 164 | max_instr = instr + 15; |
119 | 165 | ||
120 | if (user_mode(regs) && instr >= (unsigned char *)TASK_SIZE) | 166 | if (user_mode(regs) && instr >= (unsigned char *)TASK_SIZE) |
121 | return 0; | 167 | return 0; |
122 | 168 | ||
123 | while (scan_more && instr < max_instr) { | 169 | while (instr < max_instr) { |
124 | unsigned char instr_hi; | ||
125 | unsigned char instr_lo; | ||
126 | unsigned char opcode; | 170 | unsigned char opcode; |
127 | 171 | ||
128 | if (probe_kernel_address(instr, opcode)) | 172 | if (probe_kernel_address(instr, opcode)) |
129 | break; | 173 | break; |
130 | 174 | ||
131 | instr_hi = opcode & 0xf0; | ||
132 | instr_lo = opcode & 0x0f; | ||
133 | instr++; | 175 | instr++; |
134 | 176 | ||
135 | switch (instr_hi) { | 177 | if (!check_prefetch_opcode(regs, instr, opcode, &prefetch)) |
136 | case 0x20: | ||
137 | case 0x30: | ||
138 | /* | ||
139 | * Values 0x26,0x2E,0x36,0x3E are valid x86 prefixes. | ||
140 | * In X86_64 long mode, the CPU will signal invalid | ||
141 | * opcode if some of these prefixes are present so | ||
142 | * X86_64 will never get here anyway | ||
143 | */ | ||
144 | scan_more = ((instr_lo & 7) == 0x6); | ||
145 | break; | ||
146 | #ifdef CONFIG_X86_64 | ||
147 | case 0x40: | ||
148 | /* | ||
149 | * In AMD64 long mode 0x40..0x4F are valid REX prefixes | ||
150 | * Need to figure out under what instruction mode the | ||
151 | * instruction was issued. Could check the LDT for lm, | ||
152 | * but for now it's good enough to assume that long | ||
153 | * mode only uses well known segments or kernel. | ||
154 | */ | ||
155 | scan_more = (!user_mode(regs)) || (regs->cs == __USER_CS); | ||
156 | break; | ||
157 | #endif | ||
158 | case 0x60: | ||
159 | /* 0x64 thru 0x67 are valid prefixes in all modes. */ | ||
160 | scan_more = (instr_lo & 0xC) == 0x4; | ||
161 | break; | 178 | break; |
162 | case 0xF0: | ||
163 | /* 0xF0, 0xF2, 0xF3 are valid prefixes in all modes. */ | ||
164 | scan_more = !instr_lo || (instr_lo>>1) == 1; | ||
165 | break; | ||
166 | case 0x00: | ||
167 | /* Prefetch instruction is 0x0F0D or 0x0F18 */ | ||
168 | scan_more = 0; | ||
169 | |||
170 | if (probe_kernel_address(instr, opcode)) | ||
171 | break; | ||
172 | prefetch = (instr_lo == 0xF) && | ||
173 | (opcode == 0x0D || opcode == 0x18); | ||
174 | break; | ||
175 | default: | ||
176 | scan_more = 0; | ||
177 | break; | ||
178 | } | ||
179 | } | 179 | } |
180 | return prefetch; | 180 | return prefetch; |
181 | } | 181 | } |