Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | /* |
2 | * XFree86 int10 module | |
3 | * execute BIOS int 10h calls in x86 real mode environment | |
4 | * Copyright 1999 Egbert Eich | |
5 | */ | |
6 | ||
7 | #ifdef HAVE_XORG_CONFIG_H | |
8 | #include <xorg-config.h> | |
9 | #endif | |
10 | ||
11 | #include "xf86.h" | |
12 | #include "compiler.h" | |
13 | #define _INT10_PRIVATE | |
14 | #include "xf86int10.h" | |
15 | #include "int10Defines.h" | |
16 | #include "Pci.h" | |
17 | ||
18 | #define REG pInt | |
19 | ||
20 | xf86Int10InfoPtr Int10Current = NULL; | |
21 | ||
22 | static int int1A_handler(xf86Int10InfoPtr pInt); | |
23 | ||
24 | #ifndef _PC | |
25 | static int int42_handler(xf86Int10InfoPtr pInt); | |
26 | #endif | |
27 | static int intE6_handler(xf86Int10InfoPtr pInt); | |
28 | static struct pci_device *findPci(xf86Int10InfoPtr pInt, unsigned short bx); | |
29 | static CARD32 pciSlotBX(const struct pci_device *pvp); | |
30 | ||
31 | int | |
32 | int_handler(xf86Int10InfoPtr pInt) | |
33 | { | |
34 | int num = pInt->num; | |
35 | int ret = 0; | |
36 | ||
37 | switch (num) { | |
38 | #ifndef _PC | |
39 | case 0x10: | |
40 | case 0x42: | |
41 | case 0x6D: | |
42 | if (getIntVect(pInt, num) == I_S_DEFAULT_INT_VECT) | |
43 | ret = int42_handler(pInt); | |
44 | break; | |
45 | #endif | |
46 | case 0x1A: | |
47 | ret = int1A_handler(pInt); | |
48 | break; | |
49 | case 0xe6: | |
50 | ret = intE6_handler(pInt); | |
51 | break; | |
52 | default: | |
53 | break; | |
54 | } | |
55 | ||
56 | if (!ret) | |
57 | ret = run_bios_int(num, pInt); | |
58 | ||
59 | if (!ret) { | |
60 | xf86DrvMsg(pInt->pScrn->scrnIndex, X_ERROR, "Halting on int 0x%2.2x!\n", num); | |
61 | dump_registers(pInt); | |
62 | stack_trace(pInt); | |
63 | } | |
64 | ||
65 | return ret; | |
66 | } | |
67 | ||
68 | #ifndef _PC | |
69 | /* | |
70 | * This is derived from a number of PC system BIOS'es. The intent here is to | |
71 | * provide very primitive video support, before an EGA/VGA BIOS installs its | |
72 | * own interrupt vector. Here, "Ignored" calls should remain so. "Not | |
73 | * Implemented" denotes functionality that can be implemented should the need | |
74 | * arise. What are "Not Implemented" throughout are video memory accesses. | |
75 | * Also, very little input validity checking is done here. | |
76 | */ | |
77 | static int | |
78 | int42_handler(xf86Int10InfoPtr pInt) | |
79 | { | |
80 | switch (X86_AH) { | |
81 | case 0x00: | |
82 | /* Set Video Mode */ | |
83 | /* Enter: AL = video mode number */ | |
84 | /* Leave: Nothing */ | |
85 | /* Implemented (except for clearing the screen) */ | |
86 | { /* Localise */ | |
87 | unsigned int ioport; | |
88 | int i; | |
89 | CARD16 int1d, regvals, tmp; | |
90 | CARD8 mode, cgamode, cgacolour; | |
91 | ||
92 | /* | |
93 | * Ignore all mode numbers but 0x00-0x13. Some systems also ignore | |
94 | * 0x0B and 0x0C, but don't do that here. | |
95 | */ | |
96 | if (X86_AL > 0x13) | |
97 | break; | |
98 | ||
99 | /* | |
100 | * You didn't think that was really the mode set, did you? There | |
101 | * are only so many slots in the video parameter table... | |
102 | */ | |
103 | mode = X86_AL; | |
104 | ioport = 0x03D4; | |
105 | switch (MEM_RB(pInt, 0x0410) & 0x30) { | |
106 | case 0x30: /* MDA */ | |
107 | mode = 0x07; /* Force mode to 0x07 */ | |
108 | ioport = 0x03B4; | |
109 | break; | |
110 | case 0x10: /* CGA 40x25 */ | |
111 | if (mode >= 0x07) | |
112 | mode = 0x01; | |
113 | break; | |
114 | case 0x20: /* CGA 80x25 (MCGA?) */ | |
115 | if (mode >= 0x07) | |
116 | mode = 0x03; | |
117 | break; | |
118 | case 0x00: /* EGA/VGA */ | |
119 | if (mode >= 0x07) /* Don't try MDA timings */ | |
120 | mode = 0x01; /* !?!?! */ | |
121 | break; | |
122 | } | |
123 | ||
124 | /* Locate data in video parameter table */ | |
125 | int1d = MEM_RW(pInt, 0x1d << 2); | |
126 | regvals = ((mode >> 1) << 4) + int1d; | |
127 | cgacolour = 0x30; | |
128 | if (mode == 0x06) { | |
129 | regvals -= 0x10; | |
130 | cgacolour = 0x3F; | |
131 | } | |
132 | ||
133 | /** Update BIOS Data Area **/ | |
134 | ||
135 | /* Video mode */ | |
136 | MEM_WB(pInt, 0x0449, mode); | |
137 | ||
138 | /* Columns */ | |
139 | tmp = MEM_RB(pInt, mode + int1d + 0x48); | |
140 | MEM_WW(pInt, 0x044A, tmp); | |
141 | ||
142 | /* Page length */ | |
143 | tmp = MEM_RW(pInt, (mode & 0x06) + int1d + 0x40); | |
144 | MEM_WW(pInt, 0x044C, tmp); | |
145 | ||
146 | /* Start Address */ | |
147 | MEM_WW(pInt, 0x044E, 0); | |
148 | ||
149 | /* Cursor positions, one for each display page */ | |
150 | for (i = 0x0450; i < 0x0460; i += 2) | |
151 | MEM_WW(pInt, i, 0); | |
152 | ||
153 | /* Cursor start & end scanlines */ | |
154 | tmp = MEM_RB(pInt, regvals + 0x0B); | |
155 | MEM_WB(pInt, 0x0460, tmp); | |
156 | tmp = MEM_RB(pInt, regvals + 0x0A); | |
157 | MEM_WB(pInt, 0x0461, tmp); | |
158 | ||
159 | /* Current display page number */ | |
160 | MEM_WB(pInt, 0x0462, 0); | |
161 | ||
162 | /* CRTC I/O address */ | |
163 | MEM_WW(pInt, 0x0463, ioport); | |
164 | ||
165 | /* CGA Mode register value */ | |
166 | cgamode = MEM_RB(pInt, mode + int1d + 0x50); | |
167 | MEM_WB(pInt, 0x0465, cgamode); | |
168 | ||
169 | /* CGA Colour register value */ | |
170 | MEM_WB(pInt, 0x0466, cgacolour); | |
171 | ||
172 | /* Rows */ | |
173 | MEM_WB(pInt, 0x0484, (25 - 1)); | |
174 | ||
175 | /* Program the mode */ | |
176 | pci_io_write8(pInt->io, ioport + 4, cgamode & 0x37); /* Turn off screen */ | |
177 | for (i = 0; i < 0x10; i++) { | |
178 | tmp = MEM_RB(pInt, regvals + i); | |
179 | pci_io_write8(pInt->io, ioport, i); | |
180 | pci_io_write8(pInt->io, ioport + 1, tmp); | |
181 | } | |
182 | pci_io_write8(pInt->io, ioport + 5, cgacolour); /* Select colour mode */ | |
183 | pci_io_write8(pInt->io, ioport + 4, cgamode); /* Turn on screen */ | |
184 | } | |
185 | break; | |
186 | ||
187 | case 0x01: | |
188 | /* Set Cursor Type */ | |
189 | /* Enter: CH = starting line for cursor */ | |
190 | /* CL = ending line for cursor */ | |
191 | /* Leave: Nothing */ | |
192 | /* Implemented */ | |
193 | { /* Localise */ | |
194 | unsigned int ioport = MEM_RW(pInt, 0x0463); | |
195 | ||
196 | MEM_WB(pInt, 0x0460, X86_CL); | |
197 | MEM_WB(pInt, 0x0461, X86_CH); | |
198 | ||
199 | pci_io_write8(pInt->io, ioport, 0x0A); | |
200 | pci_io_write8(pInt->io, ioport + 1, X86_CH); | |
201 | pci_io_write8(pInt->io, ioport, 0x0B); | |
202 | pci_io_write8(pInt->io, ioport + 1, X86_CL); | |
203 | } | |
204 | break; | |
205 | ||
206 | case 0x02: | |
207 | /* Set Cursor Position */ | |
208 | /* Enter: BH = display page number */ | |
209 | /* DH = row */ | |
210 | /* DL = column */ | |
211 | /* Leave: Nothing */ | |
212 | /* Implemented */ | |
213 | { /* Localise */ | |
214 | unsigned int ioport; | |
215 | CARD16 offset; | |
216 | ||
217 | MEM_WB(pInt, (X86_BH << 1) + 0x0450, X86_DL); | |
218 | MEM_WB(pInt, (X86_BH << 1) + 0x0451, X86_DH); | |
219 | ||
220 | if (X86_BH != MEM_RB(pInt, 0x0462)) | |
221 | break; | |
222 | ||
223 | offset = (X86_DH * MEM_RW(pInt, 0x044A)) + X86_DL; | |
224 | offset += MEM_RW(pInt, 0x044E) << 1; | |
225 | ||
226 | ioport = MEM_RW(pInt, 0x0463); | |
227 | pci_io_write8(pInt->io, ioport, 0x0E); | |
228 | pci_io_write8(pInt->io, ioport + 1, offset >> 8); | |
229 | pci_io_write8(pInt->io, ioport, 0x0F); | |
230 | pci_io_write8(pInt->io, ioport + 1, offset & 0xFF); | |
231 | } | |
232 | break; | |
233 | ||
234 | case 0x03: | |
235 | /* Get Cursor Position */ | |
236 | /* Enter: BH = display page number */ | |
237 | /* Leave: CH = starting line for cursor */ | |
238 | /* CL = ending line for cursor */ | |
239 | /* DH = row */ | |
240 | /* DL = column */ | |
241 | /* Implemented */ | |
242 | { /* Localise */ | |
243 | X86_CL = MEM_RB(pInt, 0x0460); | |
244 | X86_CH = MEM_RB(pInt, 0x0461); | |
245 | X86_DL = MEM_RB(pInt, (X86_BH << 1) + 0x0450); | |
246 | X86_DH = MEM_RB(pInt, (X86_BH << 1) + 0x0451); | |
247 | } | |
248 | break; | |
249 | ||
250 | case 0x04: | |
251 | /* Get Light Pen Position */ | |
252 | /* Enter: Nothing */ | |
253 | /* Leave: AH = 0x01 (down/triggered) or 0x00 (not) */ | |
254 | /* BX = pixel column */ | |
255 | /* CX = pixel row */ | |
256 | /* DH = character row */ | |
257 | /* DL = character column */ | |
258 | /* Not Implemented */ | |
259 | { /* Localise */ | |
260 | xf86DrvMsgVerb(pInt->pScrn->scrnIndex, X_NOT_IMPLEMENTED, 2, | |
261 | "int 0x%2.2x(AH=0x04) -- Get Light Pen Position\n", | |
262 | pInt->num); | |
263 | if (xf86GetVerbosity() > 3) { | |
264 | dump_registers(pInt); | |
265 | stack_trace(pInt); | |
266 | } | |
267 | X86_AH = X86_BX = X86_CX = X86_DX = 0; | |
268 | } | |
269 | break; | |
270 | ||
271 | case 0x05: | |
272 | /* Set Display Page */ | |
273 | /* Enter: AL = display page number */ | |
274 | /* Leave: Nothing */ | |
275 | /* Implemented */ | |
276 | { /* Localise */ | |
277 | unsigned int ioport = MEM_RW(pInt, 0x0463); | |
278 | CARD16 start; | |
279 | CARD8 x, y; | |
280 | ||
281 | /* Calculate new start address */ | |
282 | MEM_WB(pInt, 0x0462, X86_AL); | |
283 | start = X86_AL * MEM_RW(pInt, 0x044C); | |
284 | MEM_WW(pInt, 0x044E, start); | |
285 | start <<= 1; | |
286 | ||
287 | /* Update start address */ | |
288 | pci_io_write8(pInt->io, ioport, 0x0C); | |
289 | pci_io_write8(pInt->io, ioport + 1, start >> 8); | |
290 | pci_io_write8(pInt->io, ioport, 0x0D); | |
291 | pci_io_write8(pInt->io, ioport + 1, start & 0xFF); | |
292 | ||
293 | /* Switch cursor position */ | |
294 | y = MEM_RB(pInt, (X86_AL << 1) + 0x0450); | |
295 | x = MEM_RB(pInt, (X86_AL << 1) + 0x0451); | |
296 | start += (y * MEM_RW(pInt, 0x044A)) + x; | |
297 | ||
298 | /* Update cursor position */ | |
299 | pci_io_write8(pInt->io, ioport, 0x0E); | |
300 | pci_io_write8(pInt->io, ioport + 1, start >> 8); | |
301 | pci_io_write8(pInt->io, ioport, 0x0F); | |
302 | pci_io_write8(pInt->io, ioport + 1, start & 0xFF); | |
303 | } | |
304 | break; | |
305 | ||
306 | case 0x06: | |
307 | /* Initialise or Scroll Window Up */ | |
308 | /* Enter: AL = lines to scroll up */ | |
309 | /* BH = attribute for blank */ | |
310 | /* CH = upper y of window */ | |
311 | /* CL = left x of window */ | |
312 | /* DH = lower y of window */ | |
313 | /* DL = right x of window */ | |
314 | /* Leave: Nothing */ | |
315 | /* Not Implemented */ | |
316 | { /* Localise */ | |
317 | xf86DrvMsgVerb(pInt->pScrn->scrnIndex, X_NOT_IMPLEMENTED, 2, | |
318 | "int 0x%2.2x(AH=0x06) -- Initialise or Scroll Window Up\n", | |
319 | pInt->num); | |
320 | xf86DrvMsgVerb(pInt->pScrn->scrnIndex, X_NOT_IMPLEMENTED, 3, | |
321 | " AL=0x%2.2x, BH=0x%2.2x," | |
322 | " CH=0x%2.2x, CL=0x%2.2x, DH=0x%2.2x, DL=0x%2.2x\n", | |
323 | X86_AL, X86_BH, X86_CH, X86_CL, X86_DH, X86_DL); | |
324 | if (xf86GetVerbosity() > 3) { | |
325 | dump_registers(pInt); | |
326 | stack_trace(pInt); | |
327 | } | |
328 | } | |
329 | break; | |
330 | ||
331 | case 0x07: | |
332 | /* Initialise or Scroll Window Down */ | |
333 | /* Enter: AL = lines to scroll down */ | |
334 | /* BH = attribute for blank */ | |
335 | /* CH = upper y of window */ | |
336 | /* CL = left x of window */ | |
337 | /* DH = lower y of window */ | |
338 | /* DL = right x of window */ | |
339 | /* Leave: Nothing */ | |
340 | /* Not Implemented */ | |
341 | { /* Localise */ | |
342 | xf86DrvMsgVerb(pInt->pScrn->scrnIndex, X_NOT_IMPLEMENTED, 2, | |
343 | "int 0x%2.2x(AH=0x07) -- Initialise or Scroll Window Down\n", | |
344 | pInt->num); | |
345 | xf86DrvMsgVerb(pInt->pScrn->scrnIndex, X_NOT_IMPLEMENTED, 3, | |
346 | " AL=0x%2.2x, BH=0x%2.2x," | |
347 | " CH=0x%2.2x, CL=0x%2.2x, DH=0x%2.2x, DL=0x%2.2x\n", | |
348 | X86_AL, X86_BH, X86_CH, X86_CL, X86_DH, X86_DL); | |
349 | if (xf86GetVerbosity() > 3) { | |
350 | dump_registers(pInt); | |
351 | stack_trace(pInt); | |
352 | } | |
353 | } | |
354 | break; | |
355 | ||
356 | case 0x08: | |
357 | /* Read Character and Attribute at Cursor */ | |
358 | /* Enter: BH = display page number */ | |
359 | /* Leave: AH = attribute */ | |
360 | /* AL = character */ | |
361 | /* Not Implemented */ | |
362 | { /* Localise */ | |
363 | xf86DrvMsgVerb(pInt->pScrn->scrnIndex, X_NOT_IMPLEMENTED, 2, | |
364 | "int 0x%2.2x(AH=0x08) -- Read Character and Attribute at" | |
365 | " Cursor\n", pInt->num); | |
366 | xf86DrvMsgVerb(pInt->pScrn->scrnIndex, X_NOT_IMPLEMENTED, 3, | |
367 | "BH=0x%2.2x\n", X86_BH); | |
368 | if (xf86GetVerbosity() > 3) { | |
369 | dump_registers(pInt); | |
370 | stack_trace(pInt); | |
371 | } | |
372 | X86_AX = 0; | |
373 | } | |
374 | break; | |
375 | ||
376 | case 0x09: | |
377 | /* Write Character and Attribute at Cursor */ | |
378 | /* Enter: AL = character */ | |
379 | /* BH = display page number */ | |
380 | /* BL = attribute (text) or colour (graphics) */ | |
381 | /* CX = replication count */ | |
382 | /* Leave: Nothing */ | |
383 | /* Not Implemented */ | |
384 | { /* Localise */ | |
385 | xf86DrvMsgVerb(pInt->pScrn->scrnIndex, X_NOT_IMPLEMENTED, 2, | |
386 | "int 0x%2.2x(AH=0x09) -- Write Character and Attribute at" | |
387 | " Cursor\n", pInt->num); | |
388 | xf86DrvMsgVerb(pInt->pScrn->scrnIndex, X_NOT_IMPLEMENTED, 3, | |
389 | "AL=0x%2.2x, BH=0x%2.2x, BL=0x%2.2x, CX=0x%4.4x\n", | |
390 | X86_AL, X86_BH, X86_BL, X86_CX); | |
391 | if (xf86GetVerbosity() > 3) { | |
392 | dump_registers(pInt); | |
393 | stack_trace(pInt); | |
394 | } | |
395 | } | |
396 | break; | |
397 | ||
398 | case 0x0a: | |
399 | /* Write Character at Cursor */ | |
400 | /* Enter: AL = character */ | |
401 | /* BH = display page number */ | |
402 | /* BL = colour */ | |
403 | /* CX = replication count */ | |
404 | /* Leave: Nothing */ | |
405 | /* Not Implemented */ | |
406 | { /* Localise */ | |
407 | xf86DrvMsgVerb(pInt->pScrn->scrnIndex, X_NOT_IMPLEMENTED, 2, | |
408 | "int 0x%2.2x(AH=0x0A) -- Write Character at Cursor\n", | |
409 | pInt->num); | |
410 | xf86DrvMsgVerb(pInt->pScrn->scrnIndex, X_NOT_IMPLEMENTED, 3, | |
411 | "AL=0x%2.2x, BH=0x%2.2x, BL=0x%2.2x, CX=0x%4.4x\n", | |
412 | X86_AL, X86_BH, X86_BL, X86_CX); | |
413 | if (xf86GetVerbosity() > 3) { | |
414 | dump_registers(pInt); | |
415 | stack_trace(pInt); | |
416 | } | |
417 | } | |
418 | break; | |
419 | ||
420 | case 0x0b: | |
421 | /* Set Palette, Background or Border */ | |
422 | /* Enter: BH = 0x00 or 0x01 */ | |
423 | /* BL = colour or palette (respectively) */ | |
424 | /* Leave: Nothing */ | |
425 | /* Implemented */ | |
426 | { /* Localise */ | |
427 | unsigned int ioport = MEM_RW(pInt, 0x0463) + 5; | |
428 | CARD8 cgacolour = MEM_RB(pInt, 0x0466); | |
429 | ||
430 | if (X86_BH) { | |
431 | cgacolour &= 0xDF; | |
432 | cgacolour |= (X86_BL & 0x01) << 5; | |
433 | } | |
434 | else { | |
435 | cgacolour &= 0xE0; | |
436 | cgacolour |= X86_BL & 0x1F; | |
437 | } | |
438 | ||
439 | MEM_WB(pInt, 0x0466, cgacolour); | |
440 | pci_io_write8(pInt->io, ioport, cgacolour); | |
441 | } | |
442 | break; | |
443 | ||
444 | case 0x0c: | |
445 | /* Write Graphics Pixel */ | |
446 | /* Enter: AL = pixel value */ | |
447 | /* BH = display page number */ | |
448 | /* CX = column */ | |
449 | /* DX = row */ | |
450 | /* Leave: Nothing */ | |
451 | /* Not Implemented */ | |
452 | { /* Localise */ | |
453 | xf86DrvMsgVerb(pInt->pScrn->scrnIndex, X_NOT_IMPLEMENTED, 2, | |
454 | "int 0x%2.2x(AH=0x0C) -- Write Graphics Pixel\n", | |
455 | pInt->num); | |
456 | xf86DrvMsgVerb(pInt->pScrn->scrnIndex, X_NOT_IMPLEMENTED, 3, | |
457 | "AL=0x%2.2x, BH=0x%2.2x, CX=0x%4.4x, DX=0x%4.4x\n", | |
458 | X86_AL, X86_BH, X86_CX, X86_DX); | |
459 | if (xf86GetVerbosity() > 3) { | |
460 | dump_registers(pInt); | |
461 | stack_trace(pInt); | |
462 | } | |
463 | } | |
464 | break; | |
465 | ||
466 | case 0x0d: | |
467 | /* Read Graphics Pixel */ | |
468 | /* Enter: BH = display page number */ | |
469 | /* CX = column */ | |
470 | /* DX = row */ | |
471 | /* Leave: AL = pixel value */ | |
472 | /* Not Implemented */ | |
473 | { /* Localise */ | |
474 | xf86DrvMsgVerb(pInt->pScrn->scrnIndex, X_NOT_IMPLEMENTED, 2, | |
475 | "int 0x%2.2x(AH=0x0D) -- Read Graphics Pixel\n", | |
476 | pInt->num); | |
477 | xf86DrvMsgVerb(pInt->pScrn->scrnIndex, X_NOT_IMPLEMENTED, 3, | |
478 | "BH=0x%2.2x, CX=0x%4.4x, DX=0x%4.4x\n", X86_BH, X86_CX, | |
479 | X86_DX); | |
480 | if (xf86GetVerbosity() > 3) { | |
481 | dump_registers(pInt); | |
482 | stack_trace(pInt); | |
483 | } | |
484 | X86_AL = 0; | |
485 | } | |
486 | break; | |
487 | ||
488 | case 0x0e: | |
489 | /* Write Character in Teletype Mode */ | |
490 | /* Enter: AL = character */ | |
491 | /* BH = display page number */ | |
492 | /* BL = foreground colour */ | |
493 | /* Leave: Nothing */ | |
494 | /* Not Implemented */ | |
495 | /* WARNING: Emulation of BEL characters will require */ | |
496 | /* emulation of RTC and PC speaker I/O. */ | |
497 | /* Also, this recurses through int 0x10 */ | |
498 | /* which might or might not have been */ | |
499 | /* installed yet. */ | |
500 | { /* Localise */ | |
501 | xf86DrvMsgVerb(pInt->pScrn->scrnIndex, X_NOT_IMPLEMENTED, 2, | |
502 | "int 0x%2.2x(AH=0x0E) -- Write Character in Teletype Mode\n", | |
503 | pInt->num); | |
504 | xf86DrvMsgVerb(pInt->pScrn->scrnIndex, X_NOT_IMPLEMENTED, 3, | |
505 | "AL=0x%2.2x, BH=0x%2.2x, BL=0x%2.2x\n", | |
506 | X86_AL, X86_BH, X86_BL); | |
507 | if (xf86GetVerbosity() > 3) { | |
508 | dump_registers(pInt); | |
509 | stack_trace(pInt); | |
510 | } | |
511 | } | |
512 | break; | |
513 | ||
514 | case 0x0f: | |
515 | /* Get Video Mode */ | |
516 | /* Enter: Nothing */ | |
517 | /* Leave: AH = number of columns */ | |
518 | /* AL = video mode number */ | |
519 | /* BH = display page number */ | |
520 | /* Implemented */ | |
521 | { /* Localise */ | |
522 | X86_AH = MEM_RW(pInt, 0x044A); | |
523 | X86_AL = MEM_RB(pInt, 0x0449); | |
524 | X86_BH = MEM_RB(pInt, 0x0462); | |
525 | } | |
526 | break; | |
527 | ||
528 | case 0x10: | |
529 | /* Colour Control (subfunction in AL) */ | |
530 | /* Enter: Various */ | |
531 | /* Leave: Various */ | |
532 | /* Ignored */ | |
533 | break; | |
534 | ||
535 | case 0x11: | |
536 | /* Font Control (subfunction in AL) */ | |
537 | /* Enter: Various */ | |
538 | /* Leave: Various */ | |
539 | /* Ignored */ | |
540 | break; | |
541 | ||
542 | case 0x12: | |
543 | /* Miscellaneous (subfunction in BL) */ | |
544 | /* Enter: Various */ | |
545 | /* Leave: Various */ | |
546 | /* Ignored. Previous code here optionally allowed */ | |
547 | /* the enabling and disabling of VGA, but no system */ | |
548 | /* BIOS I've come across actually implements it. */ | |
549 | break; | |
550 | ||
551 | case 0x13: | |
552 | /* Write String in Teletype Mode */ | |
553 | /* Enter: AL = write mode */ | |
554 | /* BL = attribute (if (AL & 0x02) == 0) */ | |
555 | /* CX = string length */ | |
556 | /* DH = row */ | |
557 | /* DL = column */ | |
558 | /* ES:BP = string segment:offset */ | |
559 | /* Leave: Nothing */ | |
560 | /* Not Implemented */ | |
561 | /* WARNING: Emulation of BEL characters will require */ | |
562 | /* emulation of RTC and PC speaker I/O. */ | |
563 | /* Also, this recurses through int 0x10 */ | |
564 | /* which might or might not have been */ | |
565 | /* installed yet. */ | |
566 | { /* Localise */ | |
567 | xf86DrvMsgVerb(pInt->pScrn->scrnIndex, X_NOT_IMPLEMENTED, 2, | |
568 | "int 0x%2.2x(AH=0x13) -- Write String in Teletype Mode\n", | |
569 | pInt->num); | |
570 | xf86DrvMsgVerb(pInt->pScrn->scrnIndex, X_NOT_IMPLEMENTED, 3, | |
571 | "AL=0x%2.2x, BL=0x%2.2x, CX=0x%4.4x," | |
572 | " DH=0x%2.2x, DL=0x%2.2x, ES:BP=0x%4.4x:0x%4.4x\n", | |
573 | X86_AL, X86_BL, X86_CX, X86_DH, X86_DL, X86_ES, X86_BP); | |
574 | if (xf86GetVerbosity() > 3) { | |
575 | dump_registers(pInt); | |
576 | stack_trace(pInt); | |
577 | } | |
578 | } | |
579 | break; | |
580 | ||
581 | default: | |
582 | /* Various extensions */ | |
583 | /* Enter: Various */ | |
584 | /* Leave: Various */ | |
585 | /* Ignored */ | |
586 | break; | |
587 | } | |
588 | ||
589 | return 1; | |
590 | } | |
591 | #endif | |
592 | ||
593 | #define SUCCESSFUL 0x00 | |
594 | #define DEVICE_NOT_FOUND 0x86 | |
595 | #define BAD_REGISTER_NUMBER 0x87 | |
596 | ||
597 | #ifdef SHOW_ALL_DEVICES | |
598 | /** | |
599 | * These functions are meant to be used by the PCI BIOS emulation. Some | |
600 | * BIOSes need to see if there are \b other chips of the same type around so | |
601 | * by setting \c exclude one PCI device can be explicitely excluded, if | |
602 | * required. | |
603 | */ | |
604 | static struct pci_device * | |
605 | do_find(const struct pci_id_match *m, char n, const struct pci_device *exclude) | |
606 | { | |
607 | struct pci_device *dev; | |
608 | struct pci_device_iterator *iter; | |
609 | ||
610 | n++; | |
611 | ||
612 | iter = pci_id_match_iterator_create(m); | |
613 | while ((dev = pci_device_next(iter)) != NULL) { | |
614 | if ((dev != exclude) && !(--n)) { | |
615 | break; | |
616 | } | |
617 | } | |
618 | ||
619 | pci_iterator_destroy(iter); | |
620 | ||
621 | return dev; | |
622 | } | |
623 | ||
624 | static struct pci_device * | |
625 | find_pci_device_vendor(CARD16 vendorID, CARD16 deviceID, | |
626 | char n, const struct pci_device *exclude) | |
627 | { | |
628 | struct pci_id_match m; | |
629 | ||
630 | m.vendor_id = vendorID; | |
631 | m.device_id = deviceID; | |
632 | m.subvendor_id = PCI_MATCH_ANY; | |
633 | m.subdevice_id = PCI_MATCH_ANY; | |
634 | m.device_class = 0; | |
635 | m.device_class_mask = 0; | |
636 | ||
637 | return do_find(&m, n, exclude); | |
638 | } | |
639 | ||
640 | static struct pci_device * | |
641 | find_pci_class(CARD8 intf, CARD8 subClass, CARD16 _class, | |
642 | char n, const struct pci_device *exclude) | |
643 | { | |
644 | struct pci_id_match m; | |
645 | ||
646 | m.vendor_id = PCI_MATCH_ANY; | |
647 | m.device_id = PCI_MATCH_ANY; | |
648 | m.subvendor_id = PCI_MATCH_ANY; | |
649 | m.subdevice_id = PCI_MATCH_ANY; | |
650 | m.device_class = (((uint32_t) _class) << 16) | |
651 | | (((uint32_t) subClass) << 8) | intf; | |
652 | m.device_class_mask = 0x00ffffff; | |
653 | ||
654 | return do_find(&m, n, exclude); | |
655 | } | |
656 | #endif | |
657 | ||
658 | /* | |
659 | * Return the last bus number in the same domain as dev. Only look at the | |
660 | * one domain since this is going into %cl, and VGA I/O is per-domain anyway. | |
661 | */ | |
662 | static int | |
663 | int1A_last_bus_number(struct pci_device *dev) | |
664 | { | |
665 | struct pci_device *d; | |
666 | ||
667 | struct pci_slot_match m = { dev->domain, | |
668 | PCI_MATCH_ANY, | |
669 | PCI_MATCH_ANY, | |
670 | PCI_MATCH_ANY | |
671 | }; | |
672 | struct pci_device_iterator *iter; | |
673 | int i = 0; | |
674 | ||
675 | iter = pci_slot_match_iterator_create(&m); | |
676 | ||
677 | while ((d = pci_device_next(iter))) | |
678 | if (d->bus > i) | |
679 | i = d->bus; | |
680 | ||
681 | pci_iterator_destroy(iter); | |
682 | ||
683 | return i; | |
684 | } | |
685 | ||
686 | static int | |
687 | int1A_handler(xf86Int10InfoPtr pInt) | |
688 | { | |
689 | struct pci_device *const pvp = xf86GetPciInfoForEntity(pInt->entityIndex); | |
690 | struct pci_device *dev; | |
691 | ||
692 | if (pvp == NULL) | |
693 | return 0; /* oops */ | |
694 | ||
695 | #ifdef PRINT_INT | |
696 | ErrorF("int 0x1a: ax=0x%x bx=0x%x cx=0x%x dx=0x%x di=0x%x es=0x%x\n", | |
697 | X86_EAX, X86_EBX, X86_ECX, X86_EDX, X86_EDI, X86_ESI); | |
698 | #endif | |
699 | switch (X86_AX) { | |
700 | case 0xb101: | |
701 | X86_EAX &= 0xFF00; /* no config space/special cycle support */ | |
702 | X86_EDX = 0x20494350; /* " ICP" */ | |
703 | X86_EBX = 0x0210; /* Version 2.10 */ | |
704 | X86_ECX &= 0xFF00; | |
705 | X86_ECX |= int1A_last_bus_number(pvp); | |
706 | X86_EFLAGS &= ~((unsigned long) 0x01); /* clear carry flag */ | |
707 | #ifdef PRINT_INT | |
708 | ErrorF("ax=0x%x dx=0x%x bx=0x%x cx=0x%x flags=0x%x\n", | |
709 | X86_EAX, X86_EDX, X86_EBX, X86_ECX, X86_EFLAGS); | |
710 | #endif | |
711 | return 1; | |
712 | case 0xb102: | |
713 | if ((X86_DX == pvp->vendor_id) | |
714 | && (X86_CX == pvp->device_id) | |
715 | && (X86_ESI == 0)) { | |
716 | X86_EAX = X86_AL | (SUCCESSFUL << 8); | |
717 | X86_EFLAGS &= ~((unsigned long) 0x01); /* clear carry flag */ | |
718 | X86_EBX = pciSlotBX(pvp); | |
719 | } | |
720 | #ifdef SHOW_ALL_DEVICES | |
721 | else if ((dev = find_pci_device_vendor(X86_EDX, X86_ECX, X86_ESI, pvp))) { | |
722 | X86_EAX = X86_AL | (SUCCESSFUL << 8); | |
723 | X86_EFLAGS &= ~((unsigned long) 0x01); /* clear carry flag */ | |
724 | X86_EBX = pciSlotBX(dev); | |
725 | } | |
726 | #endif | |
727 | else { | |
728 | X86_EAX = X86_AL | (DEVICE_NOT_FOUND << 8); | |
729 | X86_EFLAGS |= ((unsigned long) 0x01); /* set carry flag */ | |
730 | } | |
731 | #ifdef PRINT_INT | |
732 | ErrorF("ax=0x%x bx=0x%x flags=0x%x\n", X86_EAX, X86_EBX, X86_EFLAGS); | |
733 | #endif | |
734 | return 1; | |
735 | case 0xb103: | |
736 | if ((X86_ECX & 0x00FFFFFF) == pvp->device_class) { | |
737 | X86_EAX = X86_AL | (SUCCESSFUL << 8); | |
738 | X86_EBX = pciSlotBX(pvp); | |
739 | X86_EFLAGS &= ~((unsigned long) 0x01); /* clear carry flag */ | |
740 | } | |
741 | #ifdef SHOW_ALL_DEVICES | |
742 | else if ((dev = find_pci_class(X86_CL, X86_CH, | |
743 | (X86_ECX & 0xffff0000) >> 16, | |
744 | X86_ESI, pvp))) { | |
745 | X86_EAX = X86_AL | (SUCCESSFUL << 8); | |
746 | X86_EFLAGS &= ~((unsigned long) 0x01); /* clear carry flag */ | |
747 | X86_EBX = pciSlotBX(dev); | |
748 | } | |
749 | #endif | |
750 | else { | |
751 | X86_EAX = X86_AL | (DEVICE_NOT_FOUND << 8); | |
752 | X86_EFLAGS |= ((unsigned long) 0x01); /* set carry flag */ | |
753 | } | |
754 | #ifdef PRINT_INT | |
755 | ErrorF("ax=0x%x flags=0x%x\n", X86_EAX, X86_EFLAGS); | |
756 | #endif | |
757 | return 1; | |
758 | case 0xb108: | |
759 | if ((dev = findPci(pInt, X86_EBX)) != NULL) { | |
760 | pci_device_cfg_read_u8(dev, &X86_CL, X86_DI); | |
761 | X86_EAX = X86_AL | (SUCCESSFUL << 8); | |
762 | X86_EFLAGS &= ~((unsigned long) 0x01); /* clear carry flag */ | |
763 | } | |
764 | else { | |
765 | X86_EAX = X86_AL | (BAD_REGISTER_NUMBER << 8); | |
766 | X86_EFLAGS |= ((unsigned long) 0x01); /* set carry flag */ | |
767 | } | |
768 | #ifdef PRINT_INT | |
769 | ErrorF("ax=0x%x cx=0x%x flags=0x%x\n", X86_EAX, X86_ECX, X86_EFLAGS); | |
770 | #endif | |
771 | return 1; | |
772 | case 0xb109: | |
773 | if ((dev = findPci(pInt, X86_EBX)) != NULL) { | |
774 | pci_device_cfg_read_u16(dev, &X86_CX, X86_DI); | |
775 | X86_EAX = X86_AL | (SUCCESSFUL << 8); | |
776 | X86_EFLAGS &= ~((unsigned long) 0x01); /* clear carry flag */ | |
777 | } | |
778 | else { | |
779 | X86_EAX = X86_AL | (BAD_REGISTER_NUMBER << 8); | |
780 | X86_EFLAGS |= ((unsigned long) 0x01); /* set carry flag */ | |
781 | } | |
782 | #ifdef PRINT_INT | |
783 | ErrorF("ax=0x%x cx=0x%x flags=0x%x\n", X86_EAX, X86_ECX, X86_EFLAGS); | |
784 | #endif | |
785 | return 1; | |
786 | case 0xb10a: | |
787 | if ((dev = findPci(pInt, X86_EBX)) != NULL) { | |
788 | pci_device_cfg_read_u32(dev, &X86_ECX, X86_DI); | |
789 | X86_EAX = X86_AL | (SUCCESSFUL << 8); | |
790 | X86_EFLAGS &= ~((unsigned long) 0x01); /* clear carry flag */ | |
791 | } | |
792 | else { | |
793 | X86_EAX = X86_AL | (BAD_REGISTER_NUMBER << 8); | |
794 | X86_EFLAGS |= ((unsigned long) 0x01); /* set carry flag */ | |
795 | } | |
796 | #ifdef PRINT_INT | |
797 | ErrorF("ax=0x%x cx=0x%x flags=0x%x\n", X86_EAX, X86_ECX, X86_EFLAGS); | |
798 | #endif | |
799 | return 1; | |
800 | case 0xb10b: | |
801 | if ((dev = findPci(pInt, X86_EBX)) != NULL) { | |
802 | pci_device_cfg_write_u8(dev, X86_CL, X86_DI); | |
803 | X86_EAX = X86_AL | (SUCCESSFUL << 8); | |
804 | X86_EFLAGS &= ~((unsigned long) 0x01); /* clear carry flag */ | |
805 | } | |
806 | else { | |
807 | X86_EAX = X86_AL | (BAD_REGISTER_NUMBER << 8); | |
808 | X86_EFLAGS |= ((unsigned long) 0x01); /* set carry flag */ | |
809 | } | |
810 | #ifdef PRINT_INT | |
811 | ErrorF("ax=0x%x flags=0x%x\n", X86_EAX, X86_EFLAGS); | |
812 | #endif | |
813 | return 1; | |
814 | case 0xb10c: | |
815 | if ((dev = findPci(pInt, X86_EBX)) != NULL) { | |
816 | pci_device_cfg_write_u16(dev, X86_CX, X86_DI); | |
817 | X86_EAX = X86_AL | (SUCCESSFUL << 8); | |
818 | X86_EFLAGS &= ~((unsigned long) 0x01); /* clear carry flag */ | |
819 | } | |
820 | else { | |
821 | X86_EAX = X86_AL | (BAD_REGISTER_NUMBER << 8); | |
822 | X86_EFLAGS |= ((unsigned long) 0x01); /* set carry flag */ | |
823 | } | |
824 | #ifdef PRINT_INT | |
825 | ErrorF("ax=0x%x flags=0x%x\n", X86_EAX, X86_EFLAGS); | |
826 | #endif | |
827 | return 1; | |
828 | case 0xb10d: | |
829 | if ((dev = findPci(pInt, X86_EBX)) != NULL) { | |
830 | pci_device_cfg_write_u32(dev, X86_ECX, X86_DI); | |
831 | X86_EAX = X86_AL | (SUCCESSFUL << 8); | |
832 | X86_EFLAGS &= ~((unsigned long) 0x01); /* clear carry flag */ | |
833 | } | |
834 | else { | |
835 | X86_EAX = X86_AL | (BAD_REGISTER_NUMBER << 8); | |
836 | X86_EFLAGS |= ((unsigned long) 0x01); /* set carry flag */ | |
837 | } | |
838 | #ifdef PRINT_INT | |
839 | ErrorF("ax=0x%x flags=0x%x\n", X86_EAX, X86_EFLAGS); | |
840 | #endif | |
841 | return 1; | |
842 | default: | |
843 | xf86DrvMsgVerb(pInt->pScrn->scrnIndex, X_NOT_IMPLEMENTED, 2, | |
844 | "int 0x1a subfunction\n"); | |
845 | dump_registers(pInt); | |
846 | if (xf86GetVerbosity() > 3) | |
847 | stack_trace(pInt); | |
848 | return 0; | |
849 | } | |
850 | } | |
851 | ||
852 | static struct pci_device * | |
853 | findPci(xf86Int10InfoPtr pInt, unsigned short bx) | |
854 | { | |
855 | const unsigned bus = (bx >> 8) & 0x00FF; | |
856 | const unsigned dev = (bx >> 3) & 0x001F; | |
857 | const unsigned func = (bx) & 0x0007; | |
858 | ||
859 | return pci_device_find_by_slot(pInt->dev->domain, bus, dev, func); | |
860 | } | |
861 | ||
862 | static CARD32 | |
863 | pciSlotBX(const struct pci_device *pvp) | |
864 | { | |
865 | return ((pvp->bus << 8) & 0x00FF00) | (pvp->dev << 3) | (pvp->func); | |
866 | } | |
867 | ||
868 | /* | |
869 | * handle initialization | |
870 | */ | |
871 | static int | |
872 | intE6_handler(xf86Int10InfoPtr pInt) | |
873 | { | |
874 | struct pci_device *pvp; | |
875 | ||
876 | if ((pvp = xf86GetPciInfoForEntity(pInt->entityIndex))) | |
877 | X86_AX = (pvp->bus << 8) | (pvp->dev << 3) | (pvp->func & 0x7); | |
878 | pushw(pInt, X86_CS); | |
879 | pushw(pInt, X86_IP); | |
880 | X86_CS = pInt->BIOSseg; | |
881 | X86_EIP = 0x0003; | |
882 | X86_ES = 0; /* standard pc es */ | |
883 | return 1; | |
884 | } |