aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2009-02-20 14:37:05 -0500
committerIngo Molnar <mingo@elte.hu>2009-02-20 18:09:40 -0500
commit107a03678cac0dd6cf7095f81442a4fa477e4700 (patch)
tree5d7cb450dd33b2dd1ac3d712049250424c5bbfcd /arch
parent2d4a71676f4d89418a0d53e60b89e8b804b390b2 (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.c100
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 */
102static inline int
103check_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
102static int 149static int
103is_prefetch(struct pt_regs *regs, unsigned long error_code, unsigned long addr) 150is_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}