1 | /* |
---|
2 | * em86.c -- Include file for bootloader. |
---|
3 | * |
---|
4 | * Copyright (C) 1998, 1999 Gabriel Paubert, paubert@iram.es |
---|
5 | * |
---|
6 | * Modified to compile in RTEMS development environment |
---|
7 | * by Eric Valette |
---|
8 | * |
---|
9 | * Copyright (C) 1999 Eric Valette. valette@crf.canon.fr |
---|
10 | * |
---|
11 | * The license and distribution terms for this file may be |
---|
12 | * found in found in the file LICENSE in this distribution or at |
---|
13 | * http://www.rtems.com/license/LICENSE. |
---|
14 | * |
---|
15 | * $Id$ |
---|
16 | */ |
---|
17 | |
---|
18 | /***************************************************************************** |
---|
19 | * |
---|
20 | * Code to interpret Video BIOS ROM routines. |
---|
21 | * |
---|
22 | * |
---|
23 | ******************************************************************************/ |
---|
24 | |
---|
25 | /* These include are for the development version only */ |
---|
26 | #include <sys/types.h> |
---|
27 | #include "pci.h" |
---|
28 | #include <libcpu/byteorder.h> |
---|
29 | #ifdef __BOOT__ |
---|
30 | #include "bootldr.h" |
---|
31 | #include <limits.h> |
---|
32 | #include <rtems/bspIo.h> |
---|
33 | #endif |
---|
34 | |
---|
35 | |
---|
36 | /* Code options, put them on the compiler command line */ |
---|
37 | /* #define EIP_STATS */ /* EIP based profiling */ |
---|
38 | /* #undef EIP_STATS */ |
---|
39 | |
---|
40 | typedef union _reg_type1 { |
---|
41 | unsigned e; |
---|
42 | unsigned short x; |
---|
43 | struct { |
---|
44 | unsigned char l, h; |
---|
45 | } lh; |
---|
46 | } reg_type1; |
---|
47 | |
---|
48 | typedef union _reg_type2 { |
---|
49 | unsigned e; |
---|
50 | unsigned short x; |
---|
51 | } reg_type2; |
---|
52 | |
---|
53 | typedef struct _x86 { |
---|
54 | reg_type1 |
---|
55 | _eax, _ecx, _edx, _ebx; |
---|
56 | reg_type2 |
---|
57 | _esp, _ebp, _esi, _edi; |
---|
58 | unsigned |
---|
59 | es, cs, ss, ds, fs, gs, eip, eflags; |
---|
60 | unsigned char |
---|
61 | *esbase, *csbase, *ssbase, *dsbase, *fsbase, *gsbase; |
---|
62 | volatile unsigned char *iobase; |
---|
63 | unsigned char *ioperm; |
---|
64 | unsigned |
---|
65 | reason, nexteip, parm1, parm2, opcode, base; |
---|
66 | unsigned *optable, opreg; /* no more used! */ |
---|
67 | unsigned char* vbase; |
---|
68 | unsigned instructions; |
---|
69 | #ifdef __BOOT__ |
---|
70 | u_char * ram; |
---|
71 | u_char * rom; |
---|
72 | struct pci_dev * dev; |
---|
73 | #else |
---|
74 | unsigned filler[14]; /* Skip to next 64 byte boundary */ |
---|
75 | unsigned eipstats[32768][2]; |
---|
76 | #endif |
---|
77 | } x86; |
---|
78 | |
---|
79 | x86 v86_private __attribute__((aligned(32))); |
---|
80 | |
---|
81 | |
---|
82 | /* Emulator is in another source file */ |
---|
83 | extern |
---|
84 | void em86_enter(x86 * p); |
---|
85 | |
---|
86 | #define EAX (p->_eax.e) |
---|
87 | #define ECX (p->_ecx.e) |
---|
88 | #define EDX (p->_edx.e) |
---|
89 | #define EBX (p->_ebx.e) |
---|
90 | #define ESP (p->_esp.e) |
---|
91 | #define EBP (p->_ebp.e) |
---|
92 | #define ESI (p->_esi.e) |
---|
93 | #define EDI (p->_edi.e) |
---|
94 | #define AX (p->_eax.x) |
---|
95 | #define CX (p->_ecx.x) |
---|
96 | #define DX (p->_edx.x) |
---|
97 | #define BX (p->_ebx.x) |
---|
98 | #define SP (p->_esp.x) |
---|
99 | #define BP (p->_ebp.x) |
---|
100 | #define SI (p->_esi.x) |
---|
101 | #define DI (p->_edi.x) |
---|
102 | #define AL (p->_eax.lh.l) |
---|
103 | #define CL (p->_ecx.lh.l) |
---|
104 | #define DL (p->_edx.lh.l) |
---|
105 | #define BL (p->_ebx.lh.l) |
---|
106 | #define AH (p->_eax.lh.h) |
---|
107 | #define CH (p->_ecx.lh.h) |
---|
108 | #define DH (p->_edx.lh.h) |
---|
109 | #define BH (p->_ebx.lh.h) |
---|
110 | |
---|
111 | /* Function used to debug */ |
---|
112 | #ifdef __BOOT__ |
---|
113 | #define printf printk |
---|
114 | #endif |
---|
115 | #ifdef DEBUG |
---|
116 | static void dump86(x86 * p){ |
---|
117 | unsigned char *s = p->csbase + p->eip; |
---|
118 | printf("cs:eip=%04x:%08x, eax=%08x, ecx=%08x, edx=%08x, ebx=%08x\n", |
---|
119 | p->cs, p->eip, ld_le32(&EAX), |
---|
120 | ld_le32(&ECX), ld_le32(&EDX), ld_le32(&EBX)); |
---|
121 | printf("ss:esp=%04x:%08x, ebp=%08x, esi=%08x, edi=%08x, efl=%08x\n", |
---|
122 | p->ss, ld_le32(&ESP), ld_le32(&EBP), |
---|
123 | ld_le32(&ESI), ld_le32(&EDI), p->eflags); |
---|
124 | printf("nip=%08x, ds=%04x, es=%04x, fs=%04x, gs=%04x, total=%d\n", |
---|
125 | p->nexteip, p->ds, p->es, p->fs, p->gs, p->instructions); |
---|
126 | printf("code: %02x %02x %02x %02x %02x %02x " |
---|
127 | "%02x %02x %02x %02x %02x %02x\n", |
---|
128 | s[0], s[1], s[2], s[3], s[4], s[5], |
---|
129 | s[6], s[7], s[8], s[9], s[10], s[11]); |
---|
130 | #ifndef __BOOT__ |
---|
131 | printf("op1=%08x, op2=%08x, result=%08x, flags=%08x\n", |
---|
132 | p->filler[11], p->filler[12], p->filler[13], p->filler[14]); |
---|
133 | #endif |
---|
134 | } |
---|
135 | #else |
---|
136 | #define dump86(x) |
---|
137 | #endif |
---|
138 | |
---|
139 | int bios86pci(x86 * p) { |
---|
140 | unsigned reg=ld_le16(&DI); |
---|
141 | reg_type2 tmp; |
---|
142 | |
---|
143 | if (AL>=8 && AL<=13 && reg>0xff) { |
---|
144 | AH = PCIBIOS_BAD_REGISTER_NUMBER; |
---|
145 | } else { |
---|
146 | switch(AL) { |
---|
147 | case 2: /* find_device */ |
---|
148 | /* Should be improved for BIOS able to handle |
---|
149 | * multiple devices. We simply suppose the BIOS |
---|
150 | * inits a single device, and return an error |
---|
151 | * if it tries to find more... |
---|
152 | */ |
---|
153 | if (SI) { |
---|
154 | AH=PCIBIOS_DEVICE_NOT_FOUND; |
---|
155 | } else { |
---|
156 | BH = p->dev->bus->number; |
---|
157 | BL = p->dev->devfn; |
---|
158 | AH = 0; |
---|
159 | } |
---|
160 | break; |
---|
161 | /* |
---|
162 | case 3: find_class not implemented for now. |
---|
163 | */ |
---|
164 | case 8: /* read_config_byte */ |
---|
165 | AH=pcibios_read_config_byte(BH, BL, reg, &CL); |
---|
166 | break; |
---|
167 | case 9: /* read_config_word */ |
---|
168 | AH=pcibios_read_config_word(BH, BL, reg, &tmp.x); |
---|
169 | CX=ld_le16(&tmp.x); |
---|
170 | break; |
---|
171 | case 10: /* read_config_dword */ |
---|
172 | AH=pcibios_read_config_dword(BH, BL, reg, &tmp.e); |
---|
173 | ECX=ld_le32(&tmp.e); |
---|
174 | break; |
---|
175 | case 11: /* write_config_byte */ |
---|
176 | AH=pcibios_write_config_byte(BH, BL, reg, CL); |
---|
177 | break; |
---|
178 | case 12: /* write_config_word */ |
---|
179 | AH=pcibios_write_config_word(BH, BL, reg, ld_le16(&CX)); |
---|
180 | break; |
---|
181 | case 13: /* write_config_dword */ |
---|
182 | AH=pcibios_write_config_dword(BH, BL, reg, ld_le32(&ECX)); |
---|
183 | break; |
---|
184 | default: |
---|
185 | printf("Unimplemented or illegal PCI service call #%d!\n", |
---|
186 | AL); |
---|
187 | return 1; |
---|
188 | } |
---|
189 | } |
---|
190 | p->eip = p->nexteip; |
---|
191 | /* Set/clear carry according to result */ |
---|
192 | if (AH) p->eflags |= 1; else p->eflags &=~1; |
---|
193 | return 0; |
---|
194 | } |
---|
195 | |
---|
196 | void push2(x86 *p, unsigned value) { |
---|
197 | unsigned char * sbase= p->ssbase; |
---|
198 | unsigned newsp = (ld_le16(&SP)-2)&0xffff; |
---|
199 | st_le16(&SP,newsp); |
---|
200 | st_le16((unsigned short *)(sbase+newsp), value); |
---|
201 | } |
---|
202 | |
---|
203 | unsigned pop2(x86 *p) { |
---|
204 | unsigned char * sbase=p->ssbase; |
---|
205 | unsigned oldsp = ld_le16(&SP); |
---|
206 | st_le16(&SP,oldsp+2); |
---|
207 | return ld_le16((unsigned short *)(sbase+oldsp)); |
---|
208 | } |
---|
209 | |
---|
210 | int int10h(x86 * p) { /* Process BIOS video interrupt */ |
---|
211 | unsigned vector; |
---|
212 | vector=ld_le32((unsigned *)p->vbase+0x10); |
---|
213 | if (((vector&0xffff0000)>>16)==0xc000) { |
---|
214 | push2(p, p->eflags); |
---|
215 | push2(p, p->cs); |
---|
216 | push2(p, p->nexteip); |
---|
217 | p->cs=vector>>16; |
---|
218 | p->csbase=p->vbase + (p->cs<<4); |
---|
219 | p->eip=vector&0xffff; |
---|
220 | #if 1 |
---|
221 | p->eflags&=0xfcff; /* Clear AC/TF/IF */ |
---|
222 | #else |
---|
223 | p->eflags = (p->eflags&0xfcff)|0x100; /* Set TF for debugging */ |
---|
224 | #endif |
---|
225 | /* p->eflags|=0x100; uncomment to force a trap */ |
---|
226 | return(0); |
---|
227 | } else { |
---|
228 | switch(AH) { |
---|
229 | case 0x12: |
---|
230 | switch(BL){ |
---|
231 | case 0x32: |
---|
232 | p->eip=p->nexteip; |
---|
233 | return(0); |
---|
234 | break; |
---|
235 | default: |
---|
236 | break; |
---|
237 | } |
---|
238 | default: |
---|
239 | break; |
---|
240 | } |
---|
241 | printf("unhandled soft interrupt 0x10: vector=%x\n", vector); |
---|
242 | return(1); |
---|
243 | } |
---|
244 | } |
---|
245 | |
---|
246 | int process_softint(x86 * p) { |
---|
247 | #if 0 |
---|
248 | if (p->parm1!=0x10 || AH!=0x0e) { |
---|
249 | printf("Soft interrupt\n"); |
---|
250 | dump86(p); |
---|
251 | } |
---|
252 | #endif |
---|
253 | switch(p->parm1) { |
---|
254 | case 0x10: /* BIOS video interrupt */ |
---|
255 | return int10h(p); |
---|
256 | case 0x1a: |
---|
257 | if(AH==0xb1) return bios86pci(p); |
---|
258 | break; |
---|
259 | default: |
---|
260 | break; |
---|
261 | } |
---|
262 | dump86(p); |
---|
263 | printf("Unhandled soft interrupt number 0x%04x, AX=0x%04x\n", |
---|
264 | p->parm1, ld_le16(&AX)); |
---|
265 | return(1); |
---|
266 | } |
---|
267 | |
---|
268 | /* The only function called back by the emulator is em86_trap, all |
---|
269 | instructions may that change the code segment are trapped here. |
---|
270 | p->reason is one of the following codes. */ |
---|
271 | #define code_zerdiv 0 |
---|
272 | #define code_trap 1 |
---|
273 | #define code_int3 3 |
---|
274 | #define code_into 4 |
---|
275 | #define code_bound 5 |
---|
276 | #define code_ud 6 |
---|
277 | #define code_dna 7 |
---|
278 | |
---|
279 | #define code_iretw 256 |
---|
280 | #define code_iretl 257 |
---|
281 | #define code_lcallw 258 |
---|
282 | #define code_lcalll 259 |
---|
283 | #define code_ljmpw 260 |
---|
284 | #define code_ljmpl 261 |
---|
285 | #define code_lretw 262 |
---|
286 | #define code_lretl 263 |
---|
287 | #define code_softint 264 |
---|
288 | #define code_lock 265 /* Lock prefix */ |
---|
289 | /* Codes 1024 to 2047 are used for I/O port access instructions: |
---|
290 | - The three LSB define the port size (1, 2 or 4) |
---|
291 | - bit of weight 512 means out if set, in if clear |
---|
292 | - bit of weight 256 means ins/outs if set, in/out if clear |
---|
293 | - bit of weight 128 means use esi/edi if set, si/di if clear |
---|
294 | (only used for ins/outs instructions, always clear for in/out) |
---|
295 | */ |
---|
296 | #define code_inb 1024+1 |
---|
297 | #define code_inw 1024+2 |
---|
298 | #define code_inl 1024+4 |
---|
299 | #define code_outb 1024+512+1 |
---|
300 | #define code_outw 1024+512+2 |
---|
301 | #define code_outl 1024+512+4 |
---|
302 | #define code_insb_a16 1024+256+1 |
---|
303 | #define code_insw_a16 1024+256+2 |
---|
304 | #define code_insl_a16 1024+256+4 |
---|
305 | #define code_outsb_a16 1024+512+256+1 |
---|
306 | #define code_outsw_a16 1024+512+256+2 |
---|
307 | #define code_outsl_a16 1024+512+256+4 |
---|
308 | #define code_insb_a32 1024+256+128+1 |
---|
309 | #define code_insw_a32 1024+256+128+2 |
---|
310 | #define code_insl_a32 1024+256+128+4 |
---|
311 | #define code_outsb_a32 1024+512+256+128+1 |
---|
312 | #define code_outsw_a32 1024+512+256+128+2 |
---|
313 | #define code_outsl_a32 1024+512+256+128+4 |
---|
314 | |
---|
315 | int em86_trap(x86 *p) { |
---|
316 | #ifndef __BOOT__ |
---|
317 | int i; |
---|
318 | unsigned char command[80]; |
---|
319 | unsigned char *verb, *t; |
---|
320 | unsigned short *fp; |
---|
321 | static unsigned char def=0; |
---|
322 | static unsigned char * bptaddr=NULL; /* Breakpoint address */ |
---|
323 | static unsigned char bptopc; /* Replaced breakpoint opcode */ |
---|
324 | unsigned char cmd; |
---|
325 | unsigned tmp; |
---|
326 | #endif |
---|
327 | switch(p->reason) { |
---|
328 | case code_int3: |
---|
329 | #ifndef __BOOT__ |
---|
330 | if(p->csbase+p->eip == bptaddr) { |
---|
331 | *bptaddr=bptopc; |
---|
332 | bptaddr=NULL; |
---|
333 | } |
---|
334 | else printf("Unexpected "); |
---|
335 | #endif |
---|
336 | printf("Breakpoint Interrupt !\n"); |
---|
337 | /* Note that this fallthrough (no break;) is on purpose */ |
---|
338 | #ifdef __BOOT__ |
---|
339 | return 0; |
---|
340 | #else |
---|
341 | case code_trap: |
---|
342 | dump86(p); |
---|
343 | for(;;) { |
---|
344 | printf("b(reakpoint, g(o, q(uit, s(tack, t(race ? [%c] ", def); |
---|
345 | fgets(command,sizeof(command),stdin); |
---|
346 | verb = strtok(command," \n"); |
---|
347 | if(verb) cmd=*verb; else cmd=def; |
---|
348 | def=0; |
---|
349 | switch(cmd) { |
---|
350 | case 'b': |
---|
351 | case 'B': |
---|
352 | if(bptaddr) *bptaddr=bptopc; |
---|
353 | t=strtok(0," \n"); |
---|
354 | i=sscanf(t,"%x",&tmp); |
---|
355 | if(i==1) { |
---|
356 | bptaddr=p->vbase + tmp; |
---|
357 | bptopc=*bptaddr; |
---|
358 | *bptaddr=0xcc; |
---|
359 | } else bptaddr=NULL; |
---|
360 | break; |
---|
361 | case 'q': |
---|
362 | case 'Q': |
---|
363 | return 1; |
---|
364 | break; |
---|
365 | |
---|
366 | case 'g': |
---|
367 | case 'G': |
---|
368 | p->eflags &= ~0x100; |
---|
369 | return 0; |
---|
370 | break; |
---|
371 | |
---|
372 | case 's': |
---|
373 | case 'S': /* Print the 8 stack top words */ |
---|
374 | fp = (unsigned short *)(p->ssbase+ld_le16(&SP)); |
---|
375 | printf("Stack [%04x:%04x]: %04x %04x %04x %04x %04x %04x %04x %04x\n", |
---|
376 | p->ss, ld_le16(&SP), |
---|
377 | ld_le16(fp+0), ld_le16(fp+1), ld_le16(fp+2), ld_le16(fp+3), |
---|
378 | ld_le16(fp+4), ld_le16(fp+5), ld_le16(fp+6), ld_le16(fp+7)); |
---|
379 | break; |
---|
380 | case 't': |
---|
381 | case 'T': |
---|
382 | p->eflags |= 0x10100; /* Set the resume and trap flags */ |
---|
383 | def='t'; |
---|
384 | return 0; |
---|
385 | break; |
---|
386 | /* Should add some code to edit registers */ |
---|
387 | } |
---|
388 | } |
---|
389 | #endif |
---|
390 | break; |
---|
391 | case code_ud: |
---|
392 | printf("Attempt to execute an unimplemented" |
---|
393 | "or undefined opcode!\n"); |
---|
394 | dump86(p); |
---|
395 | return(1); /* exit interpreter */ |
---|
396 | break; |
---|
397 | case code_dna: |
---|
398 | printf("Attempt to execute a floating point instruction!\n"); |
---|
399 | dump86(p); |
---|
400 | return(1); |
---|
401 | break; |
---|
402 | case code_softint: |
---|
403 | return process_softint(p); |
---|
404 | break; |
---|
405 | case code_iretw: |
---|
406 | p->eip=pop2(p); |
---|
407 | p->cs=pop2(p); |
---|
408 | p->csbase=p->vbase + (p->cs<<4); |
---|
409 | p->eflags= (p->eflags&0xfffe0000)|pop2(p); |
---|
410 | /* p->eflags|= 0x100; */ /* Uncomment to trap after iretws */ |
---|
411 | return(0); |
---|
412 | break; |
---|
413 | #ifndef __BOOT__ |
---|
414 | case code_inb: |
---|
415 | case code_inw: |
---|
416 | case code_inl: |
---|
417 | case code_insb_a16: |
---|
418 | case code_insw_a16: |
---|
419 | case code_insl_a16: |
---|
420 | case code_insb_a32: |
---|
421 | case code_insw_a32: |
---|
422 | case code_insl_a32: |
---|
423 | case code_outb: |
---|
424 | case code_outw: |
---|
425 | case code_outl: |
---|
426 | case code_outsb_a16: |
---|
427 | case code_outsw_a16: |
---|
428 | case code_outsl_a16: |
---|
429 | case code_outsb_a32: |
---|
430 | case code_outsw_a32: |
---|
431 | case code_outsl_a32: |
---|
432 | /* For now we simply enable I/O to the ports and continue */ |
---|
433 | for(i=p->parm1; i<p->parm1+(p->reason&7); i++) { |
---|
434 | p->ioperm[i/8] &= ~(1<<i%8); |
---|
435 | } |
---|
436 | printf("Access to ports %04x-%04x enabled.\n", |
---|
437 | p->parm1, p->parm1+(p->reason&7)-1); |
---|
438 | return(0); |
---|
439 | #endif |
---|
440 | case code_lretw: |
---|
441 | /* Check for the exit eyecatcher */ |
---|
442 | if ( *(u_int *)(p->ssbase+ld_le16(&SP)) == UINT_MAX) return 1; |
---|
443 | /* No break on purpose */ |
---|
444 | default: |
---|
445 | dump86(p); |
---|
446 | printf("em86_trap called with unhandled reason code !\n"); |
---|
447 | return(1); |
---|
448 | |
---|
449 | } |
---|
450 | } |
---|
451 | |
---|
452 | void cleanup_v86_mess(void) { |
---|
453 | x86 *p = (x86 *) bd->v86_private; |
---|
454 | |
---|
455 | /* This automatically removes the mappings ! */ |
---|
456 | vfree(p->vbase); |
---|
457 | p->vbase = 0; |
---|
458 | pfree(p->ram); |
---|
459 | p->ram = 0; |
---|
460 | sfree(p->ioperm); |
---|
461 | p->ioperm=0; |
---|
462 | } |
---|
463 | |
---|
464 | |
---|
465 | int init_v86(void) { |
---|
466 | x86 *p = (x86 *) bd->v86_private; |
---|
467 | |
---|
468 | /* p->vbase is non null when the v86 is properly set-up */ |
---|
469 | if (p->vbase) return 0; |
---|
470 | |
---|
471 | /* Set everything to 0 */ |
---|
472 | memset(p, 0, sizeof(*p)); |
---|
473 | p->ioperm = salloc(65536/8+1); |
---|
474 | p->ram = palloc(0xa0000); |
---|
475 | p->iobase = ptr_mem_map->io_base; |
---|
476 | |
---|
477 | if (!p->ram || !p->ioperm) return 1; |
---|
478 | |
---|
479 | /* The ioperm array must have an additional byte at the end ! */ |
---|
480 | p->ioperm[65536/8] = 0xff; |
---|
481 | |
---|
482 | p->vbase = valloc(0x110000); |
---|
483 | if (!p->vbase) return 1; |
---|
484 | |
---|
485 | /* These calls should never fail. */ |
---|
486 | vmap(p->vbase, (u_long)p->ram|PTE_RAM, 0xa0000); |
---|
487 | vmap(p->vbase+0x100000, (u_long)p->ram|PTE_RAM, 0x10000); |
---|
488 | vmap(p->vbase+0xa0000, |
---|
489 | ((u_long)ptr_mem_map->isa_mem_base+0xa0000)|PTE_IO, 0x20000); |
---|
490 | return 0; |
---|
491 | } |
---|
492 | |
---|
493 | void em86_main(struct pci_dev *dev){ |
---|
494 | x86 *p = (x86 *) bd->v86_private; |
---|
495 | u_short signature; |
---|
496 | u_char length; |
---|
497 | volatile u_int *src; |
---|
498 | u_int *dst, left, saved_rom; |
---|
499 | #if defined(MONITOR_IO) && !defined(__BOOT__) |
---|
500 | #define IOMASK 0xff |
---|
501 | #else |
---|
502 | #define IOMASK 0 |
---|
503 | #endif |
---|
504 | |
---|
505 | |
---|
506 | #ifndef __BOOT__ |
---|
507 | int i; |
---|
508 | /* Allow or disable access to all ports */ |
---|
509 | for(i=0; i<65536/8; i++) p->ioperm[i]=IOMASK; |
---|
510 | p->ioperm[i] = 0xff; /* Last unused byte must have this value */ |
---|
511 | #endif |
---|
512 | p->dev = dev; |
---|
513 | memset(p->vbase, 0, 0xa0000); |
---|
514 | /* Set up a few registers */ |
---|
515 | p->cs = 0xc000; p->csbase = p->vbase + 0xc0000; |
---|
516 | p->ss = 0x1000; p->ssbase = p->vbase + 0x10000; |
---|
517 | p->eflags=0x200; |
---|
518 | st_le16(&SP,0xfffc); p->eip=3; |
---|
519 | |
---|
520 | p->dsbase = p->esbase = p->fsbase = p->gsbase = p->vbase; |
---|
521 | |
---|
522 | /* Follow the PCI BIOS specification */ |
---|
523 | AH=dev->bus->number; |
---|
524 | AL=dev->devfn; |
---|
525 | |
---|
526 | /* All other registers are irrelevant except ES:DI which |
---|
527 | * should point to a PnP installation check block. This |
---|
528 | * is not yet implemented due to lack of references. */ |
---|
529 | |
---|
530 | /* Store a return address of 0xffff:0xffff as eyecatcher */ |
---|
531 | *(u_int *)(p->ssbase+ld_le16(&SP)) = UINT_MAX; |
---|
532 | |
---|
533 | /* Interrupt for BIOS EGA services is 0xf000:0xf065 (int 0x10) */ |
---|
534 | st_le32((u_int *)p->vbase + 0x10, 0xf000f065); |
---|
535 | |
---|
536 | /* Enable the ROM, read it and disable it immediately */ |
---|
537 | pci_read_config_dword(dev, PCI_ROM_ADDRESS, &saved_rom); |
---|
538 | pci_write_config_dword(dev, PCI_ROM_ADDRESS, 0x000c0001); |
---|
539 | |
---|
540 | /* Check that there is an Intel ROM. Should we also check that |
---|
541 | * the first instruction is a jump (0xe9 or 0xeb) ? |
---|
542 | */ |
---|
543 | signature = *(u_short *)(ptr_mem_map->isa_mem_base+0xc0000); |
---|
544 | if (signature!=0x55aa) { |
---|
545 | printf("bad signature: %04x.\n", signature); |
---|
546 | return; |
---|
547 | } |
---|
548 | /* Allocate memory and copy the video rom to vbase+0xc0000; */ |
---|
549 | length = ptr_mem_map->isa_mem_base[0xc0002]; |
---|
550 | p->rom = palloc(length*512); |
---|
551 | if (!p->rom) return; |
---|
552 | |
---|
553 | |
---|
554 | for(dst=(u_int *) p->rom, |
---|
555 | src=(volatile u_int *)(ptr_mem_map->isa_mem_base+0xc0000), |
---|
556 | left = length*512/sizeof(u_int); |
---|
557 | left--; |
---|
558 | *dst++=*src++); |
---|
559 | |
---|
560 | /* Disable the ROM and map the copy in virtual address space, note |
---|
561 | * that the ROM has to be mapped as RAM since some BIOSes (at least |
---|
562 | * Cirrus) perform write accesses to their own ROM. The reason seems |
---|
563 | * to be that they check that they must execute from shadow RAM |
---|
564 | * because accessing the ROM prevents accessing the video RAM |
---|
565 | * according to comments in linux/arch/alpha/kernel/bios32.c. |
---|
566 | */ |
---|
567 | |
---|
568 | pci_write_config_dword(dev, PCI_ROM_ADDRESS, saved_rom); |
---|
569 | vmap(p->vbase+0xc0000, (u_long)p->rom|PTE_RAM, length*512); |
---|
570 | |
---|
571 | /* Now actually emulate the ROM init routine */ |
---|
572 | em86_enter(p); |
---|
573 | |
---|
574 | /* Free the acquired resources */ |
---|
575 | vunmap(p->vbase+0xc0000); |
---|
576 | pfree(p->rom); |
---|
577 | } |
---|
578 | |
---|
579 | |
---|
580 | |
---|
581 | |
---|
582 | |
---|