Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | #ifdef HAVE_XORG_CONFIG_H |
2 | #include <xorg-config.h> | |
3 | #endif | |
4 | ||
5 | #include <errno.h> | |
6 | #include <string.h> | |
7 | ||
8 | #include "xf86.h" | |
9 | #include "xf86_OSproc.h" | |
10 | #include "xf86Pci.h" | |
11 | #include "compiler.h" | |
12 | #define _INT10_PRIVATE | |
13 | #include "xf86int10.h" | |
14 | ||
15 | #define REG pInt | |
16 | ||
17 | #ifdef _VM86_LINUX | |
18 | #include "int10Defines.h" | |
19 | ||
20 | static int vm86_rep(struct vm86_struct *ptr); | |
21 | static struct vm86_struct vm86_s; | |
22 | ||
23 | Bool | |
24 | xf86Int10ExecSetup(xf86Int10InfoPtr pInt) | |
25 | { | |
26 | #define VM86S ((struct vm86_struct *)pInt->cpuRegs) | |
27 | ||
28 | pInt->cpuRegs = &vm86_s; | |
29 | VM86S->flags = 0; | |
30 | VM86S->screen_bitmap = 0; | |
31 | VM86S->cpu_type = CPU_586; | |
32 | memset(&VM86S->int_revectored, 0xff, sizeof(VM86S->int_revectored)); | |
33 | memset(&VM86S->int21_revectored, 0xff, sizeof(VM86S->int21_revectored)); | |
34 | return TRUE; | |
35 | } | |
36 | ||
37 | /* get the linear address */ | |
38 | #define LIN_PREF_SI ((pref_seg << 4) + X86_SI) | |
39 | #define LWECX ((prefix66 ^ prefix67) ? X86_ECX : X86_CX) | |
40 | #define LWECX_ZERO {if (prefix66 ^ prefix67) X86_ECX = 0; else X86_CX = 0;} | |
41 | #define DF (1 << 10) | |
42 | ||
43 | /* vm86 fault handling */ | |
44 | static Bool | |
45 | vm86_GP_fault(xf86Int10InfoPtr pInt) | |
46 | { | |
47 | unsigned char *csp, *lina; | |
48 | CARD32 org_eip; | |
49 | int pref_seg; | |
50 | int done, is_rep, prefix66, prefix67; | |
51 | ||
52 | csp = lina = SEG_ADR((unsigned char *), X86_CS, IP); | |
53 | ||
54 | is_rep = 0; | |
55 | prefix66 = prefix67 = 0; | |
56 | pref_seg = -1; | |
57 | ||
58 | /* eat up prefixes */ | |
59 | done = 0; | |
60 | do { | |
61 | switch (MEM_RB(pInt, (int) csp++)) { | |
62 | case 0x66: /* operand prefix */ | |
63 | prefix66 = 1; | |
64 | break; | |
65 | case 0x67: /* address prefix */ | |
66 | prefix67 = 1; | |
67 | break; | |
68 | case 0x2e: /* CS */ | |
69 | pref_seg = X86_CS; | |
70 | break; | |
71 | case 0x3e: /* DS */ | |
72 | pref_seg = X86_DS; | |
73 | break; | |
74 | case 0x26: /* ES */ | |
75 | pref_seg = X86_ES; | |
76 | break; | |
77 | case 0x36: /* SS */ | |
78 | pref_seg = X86_SS; | |
79 | break; | |
80 | case 0x65: /* GS */ | |
81 | pref_seg = X86_GS; | |
82 | break; | |
83 | case 0x64: /* FS */ | |
84 | pref_seg = X86_FS; | |
85 | break; | |
86 | case 0xf0: /* lock */ | |
87 | break; | |
88 | case 0xf2: /* repnz */ | |
89 | case 0xf3: /* rep */ | |
90 | is_rep = 1; | |
91 | break; | |
92 | default: | |
93 | done = 1; | |
94 | } | |
95 | } while (!done); | |
96 | csp--; /* oops one too many */ | |
97 | org_eip = X86_EIP; | |
98 | X86_IP += (csp - lina); | |
99 | ||
100 | switch (MEM_RB(pInt, (int) csp)) { | |
101 | case 0x6c: /* insb */ | |
102 | /* NOTE: ES can't be overwritten; prefixes 66,67 should use esi,edi,ecx | |
103 | * but is anyone using extended regs in real mode? */ | |
104 | /* WARNING: no test for DI wrapping! */ | |
105 | X86_EDI += port_rep_inb(pInt, X86_DX, SEG_EADR((CARD32), X86_ES, DI), | |
106 | X86_FLAGS & DF, is_rep ? LWECX : 1); | |
107 | if (is_rep) | |
108 | LWECX_ZERO; | |
109 | X86_IP++; | |
110 | break; | |
111 | ||
112 | case 0x6d: /* (rep) insw / insd */ | |
113 | /* NOTE: ES can't be overwritten */ | |
114 | /* WARNING: no test for _DI wrapping! */ | |
115 | if (prefix66) { | |
116 | X86_DI += port_rep_inl(pInt, X86_DX, SEG_ADR((CARD32), X86_ES, DI), | |
117 | X86_EFLAGS & DF, is_rep ? LWECX : 1); | |
118 | } | |
119 | else { | |
120 | X86_DI += port_rep_inw(pInt, X86_DX, SEG_ADR((CARD32), X86_ES, DI), | |
121 | X86_FLAGS & DF, is_rep ? LWECX : 1); | |
122 | } | |
123 | if (is_rep) | |
124 | LWECX_ZERO; | |
125 | X86_IP++; | |
126 | break; | |
127 | ||
128 | case 0x6e: /* (rep) outsb */ | |
129 | if (pref_seg < 0) | |
130 | pref_seg = X86_DS; | |
131 | /* WARNING: no test for _SI wrapping! */ | |
132 | X86_SI += port_rep_outb(pInt, X86_DX, (CARD32) LIN_PREF_SI, | |
133 | X86_FLAGS & DF, is_rep ? LWECX : 1); | |
134 | if (is_rep) | |
135 | LWECX_ZERO; | |
136 | X86_IP++; | |
137 | break; | |
138 | ||
139 | case 0x6f: /* (rep) outsw / outsd */ | |
140 | if (pref_seg < 0) | |
141 | pref_seg = X86_DS; | |
142 | /* WARNING: no test for _SI wrapping! */ | |
143 | if (prefix66) { | |
144 | X86_SI += port_rep_outl(pInt, X86_DX, (CARD32) LIN_PREF_SI, | |
145 | X86_EFLAGS & DF, is_rep ? LWECX : 1); | |
146 | } | |
147 | else { | |
148 | X86_SI += port_rep_outw(pInt, X86_DX, (CARD32) LIN_PREF_SI, | |
149 | X86_FLAGS & DF, is_rep ? LWECX : 1); | |
150 | } | |
151 | if (is_rep) | |
152 | LWECX_ZERO; | |
153 | X86_IP++; | |
154 | break; | |
155 | ||
156 | case 0xe5: /* inw xx, inl xx */ | |
157 | if (prefix66) | |
158 | X86_EAX = x_inl(csp[1]); | |
159 | else | |
160 | X86_AX = x_inw(csp[1]); | |
161 | X86_IP += 2; | |
162 | break; | |
163 | ||
164 | case 0xe4: /* inb xx */ | |
165 | X86_AL = x_inb(csp[1]); | |
166 | X86_IP += 2; | |
167 | break; | |
168 | ||
169 | case 0xed: /* inw dx, inl dx */ | |
170 | if (prefix66) | |
171 | X86_EAX = x_inl(X86_DX); | |
172 | else | |
173 | X86_AX = x_inw(X86_DX); | |
174 | X86_IP += 1; | |
175 | break; | |
176 | ||
177 | case 0xec: /* inb dx */ | |
178 | X86_AL = x_inb(X86_DX); | |
179 | X86_IP += 1; | |
180 | break; | |
181 | ||
182 | case 0xe7: /* outw xx */ | |
183 | if (prefix66) | |
184 | x_outl(csp[1], X86_EAX); | |
185 | else | |
186 | x_outw(csp[1], X86_AX); | |
187 | X86_IP += 2; | |
188 | break; | |
189 | ||
190 | case 0xe6: /* outb xx */ | |
191 | x_outb(csp[1], X86_AL); | |
192 | X86_IP += 2; | |
193 | break; | |
194 | ||
195 | case 0xef: /* outw dx */ | |
196 | if (prefix66) | |
197 | x_outl(X86_DX, X86_EAX); | |
198 | else | |
199 | x_outw(X86_DX, X86_AX); | |
200 | X86_IP += 1; | |
201 | break; | |
202 | ||
203 | case 0xee: /* outb dx */ | |
204 | x_outb(X86_DX, X86_AL); | |
205 | X86_IP += 1; | |
206 | break; | |
207 | ||
208 | case 0xf4: | |
209 | DebugF("hlt at %p\n", lina); | |
210 | return FALSE; | |
211 | ||
212 | case 0x0f: | |
213 | xf86DrvMsg(pInt->pScrn->scrnIndex, X_ERROR, | |
214 | "CPU 0x0f Trap at CS:EIP=0x%4.4x:0x%8.8lx\n", X86_CS, | |
215 | X86_EIP); | |
216 | goto op0ferr; | |
217 | ||
218 | default: | |
219 | xf86DrvMsg(pInt->pScrn->scrnIndex, X_ERROR, "unknown reason for exception\n"); | |
220 | ||
221 | op0ferr: | |
222 | dump_registers(pInt); | |
223 | stack_trace(pInt); | |
224 | dump_code(pInt); | |
225 | xf86DrvMsg(pInt->pScrn->scrnIndex, X_ERROR, "cannot continue\n"); | |
226 | return FALSE; | |
227 | } /* end of switch() */ | |
228 | return TRUE; | |
229 | } | |
230 | ||
231 | static int | |
232 | do_vm86(xf86Int10InfoPtr pInt) | |
233 | { | |
234 | int retval, signo; | |
235 | ||
236 | xf86InterceptSignals(&signo); | |
237 | retval = vm86_rep(VM86S); | |
238 | xf86InterceptSignals(NULL); | |
239 | ||
240 | if (signo >= 0) { | |
241 | xf86DrvMsg(pInt->pScrn->scrnIndex, X_ERROR, | |
242 | "vm86() syscall generated signal %d.\n", signo); | |
243 | dump_registers(pInt); | |
244 | dump_code(pInt); | |
245 | stack_trace(pInt); | |
246 | return 0; | |
247 | } | |
248 | ||
249 | switch (VM86_TYPE(retval)) { | |
250 | case VM86_UNKNOWN: | |
251 | if (!vm86_GP_fault(pInt)) | |
252 | return 0; | |
253 | break; | |
254 | case VM86_STI: | |
255 | xf86DrvMsg(pInt->pScrn->scrnIndex, X_ERROR, "vm86_sti :-((\n"); | |
256 | dump_registers(pInt); | |
257 | dump_code(pInt); | |
258 | stack_trace(pInt); | |
259 | return 0; | |
260 | case VM86_INTx: | |
261 | pInt->num = VM86_ARG(retval); | |
262 | if (!int_handler(pInt)) { | |
263 | xf86DrvMsg(pInt->pScrn->scrnIndex, X_ERROR, | |
264 | "Unknown vm86_int: 0x%X\n\n", VM86_ARG(retval)); | |
265 | dump_registers(pInt); | |
266 | dump_code(pInt); | |
267 | stack_trace(pInt); | |
268 | return 0; | |
269 | } | |
270 | /* I'm not sure yet what to do if we can handle ints */ | |
271 | break; | |
272 | case VM86_SIGNAL: | |
273 | return 1; | |
274 | /* | |
275 | * we used to warn here and bail out - but now the sigio stuff | |
276 | * always fires signals at us. So we just ignore them for now. | |
277 | */ | |
278 | xf86DrvMsg(pInt->pScrn->scrnIndex, X_WARNING, "received signal\n"); | |
279 | return 0; | |
280 | default: | |
281 | xf86DrvMsg(pInt->pScrn->scrnIndex, X_ERROR, "unknown type(0x%x)=0x%x\n", | |
282 | VM86_ARG(retval), VM86_TYPE(retval)); | |
283 | dump_registers(pInt); | |
284 | dump_code(pInt); | |
285 | stack_trace(pInt); | |
286 | return 0; | |
287 | } | |
288 | ||
289 | return 1; | |
290 | } | |
291 | ||
292 | void | |
293 | xf86ExecX86int10(xf86Int10InfoPtr pInt) | |
294 | { | |
295 | int sig = setup_int(pInt); | |
296 | ||
297 | if (int_handler(pInt)) | |
298 | while (do_vm86(pInt)) { | |
299 | }; | |
300 | ||
301 | finish_int(pInt, sig); | |
302 | } | |
303 | ||
304 | static int | |
305 | vm86_rep(struct vm86_struct *ptr) | |
306 | { | |
307 | int __res; | |
308 | ||
309 | #ifdef __PIC__ | |
310 | /* When compiling with -fPIC, we can't use asm constraint "b" because | |
311 | %ebx is already taken by gcc. */ | |
312 | __asm__ __volatile__("pushl %%ebx\n\t" | |
313 | "push %%gs\n\t" | |
314 | "movl %2,%%ebx\n\t" | |
315 | "movl %1,%%eax\n\t" | |
316 | "int $0x80\n\t" "pop %%gs\n\t" "popl %%ebx":"=a"(__res) | |
317 | :"n"((int) 113), "r"((struct vm86_struct *) ptr)); | |
318 | #else | |
319 | __asm__ __volatile__("push %%gs\n\t" | |
320 | "int $0x80\n\t" | |
321 | "pop %%gs":"=a"(__res):"a"((int) 113), | |
322 | "b"((struct vm86_struct *) ptr)); | |
323 | #endif | |
324 | ||
325 | if (__res < 0) { | |
326 | errno = -__res; | |
327 | __res = -1; | |
328 | } | |
329 | else | |
330 | errno = 0; | |
331 | return __res; | |
332 | } | |
333 | ||
334 | #endif |