]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - board/MAI/bios_emulator/x86interface.c
* Patch by Thomas Frieden, 13 Nov 2002:
[karo-tx-uboot.git] / board / MAI / bios_emulator / x86interface.c
1 #include "x86emu.h"
2 #include "glue.h"
3
4
5 /*
6  * This isn't nice, but there are a lot of incompatibilities in the U-Boot and scitech include
7  * files that this is the only really workable solution.
8  * Might be cleaned out later. 
9  */
10
11 #ifdef DEBUG
12 #undef DEBUG
13 #endif
14
15 #undef IO_LOGGING
16 #undef MEM_LOGGING
17
18 #ifdef IO_LOGGING
19 #define LOGIO(port, format, args...) if (dolog(port)) _printf(format , ## args)
20 #else
21 #define LOGIO(port, format, args...)
22 #endif
23
24 #ifdef MEM_LOGGIN
25 #define LOGMEM(format, args...) _printf(format , ## args)
26 #else
27 #define LOGMEM(format, args...)
28 #endif
29
30 #ifdef DEBUG
31 #define PRINTF(format, args...) _printf(format , ## args)
32 #else
33 #define PRINTF(format, argc...)
34 #endif
35
36 typedef unsigned char UBYTE;
37 typedef unsigned short UWORD;
38 typedef unsigned long ULONG;
39
40 typedef char BYTE;
41 typedef short WORT;
42 typedef long LONG;
43
44 #define EMULATOR_MEM_SIZE       (1024*1024)
45 #define EMULATOR_BIOS_OFFSET    0xC0000
46 #define EMULATOR_STRAP_OFFSET   0x30000
47 #define EMULATOR_STACK_OFFSET   0x20000
48 #define EMULATOR_LOGO_OFFSET    0x40000 // If you change this, change the strap code, too
49 #define VIDEO_BASE (void *)0xFD0B8000
50
51 extern char *getenv(char *);
52 extern int tstc(void);
53 extern int getc(void);
54 extern unsigned char video_get_attr(void);
55
56 int atoi(char *string)
57 {
58     int res = 0;
59     while (*string>='0' && *string <='9')
60     {
61         res *= 10;
62         res += *string-'0';
63         string++;
64     }
65
66     return res;
67 }
68
69 void cons_gets(char *buffer)
70 {
71     int i = 0;
72     char c = 0;
73
74     buffer[0] = 0;
75     if (getenv("x86_runthru")) return; //FIXME:
76     while (c != 0x0D && c != 0x0A)
77     {
78         while (!tstc());
79         c = getc();
80         if (c>=32 && c < 127)
81         {
82             buffer[i] = c;
83             i++;
84             buffer[i] = 0;
85             putc(c);
86         }
87         else
88         {
89             if (c == 0x08)
90             {
91                 if (i>0) i--;
92                 buffer[i] = 0;
93             }
94         }
95     }
96     buffer[i] = '\n';
97     buffer[i+1] = 0;
98 }
99
100 char *bios_date = "08/14/02";
101 UBYTE model = 0xFC;
102 UBYTE submodel = 0x00;
103
104 static inline UBYTE read_byte(volatile UBYTE* from)
105 {
106     int x;
107     asm volatile ("lbz %0,%1\n eieio" : "=r" (x) : "m" (*from));
108     return (UBYTE)x;
109 }
110
111 static inline void write_byte(volatile UBYTE *to, int x)
112 {
113     asm volatile ("stb %1,%0\n eieio" : "=m" (*to) : "r" (x));
114 }
115
116 static inline UWORD read_word_little(volatile UWORD *from)
117 {
118     int x;
119     asm volatile ("lhbrx %0,0,%1\n eieio" : "=r" (x) : "r" (from), "m" (*from));
120     return (UWORD)x;
121 }
122
123 static inline UWORD read_word_big(volatile UWORD *from)
124 {
125     int x;
126     asm volatile ("lhz %0,%1\n eieio" : "=r" (x) : "m" (*from));
127     return (UWORD)x;
128 }
129
130 static inline void write_word_little(volatile UWORD *to, int x)
131 {
132     asm volatile ("sthbrx %1,0,%2\n eieio" : "=m" (*to) : "r" (x), "r" (to));
133 }
134
135 static inline void write_word_big(volatile UWORD *to, int x)
136 {
137     asm volatile ("sth %1,%0\n eieio" : "=m" (*to) : "r" (x));
138 }
139
140 static inline ULONG read_long_little(volatile ULONG *from)
141 {
142     unsigned long x;
143     asm volatile ("lwbrx %0,0,%1\n eieio" : "=r" (x) : "r" (from), "m"(*from));
144     return (ULONG)x;
145 }
146
147 static inline ULONG read_long_big(volatile ULONG *from)
148 {
149     unsigned long x;
150     asm volatile ("lwz %0,%1\n eieio" : "=r" (x) : "m" (*from));
151     return (ULONG)x;
152 }
153
154 static inline void write_long_little(volatile ULONG *to, ULONG x)
155 {
156     asm volatile ("stwbrx %1,0,%2\n eieio" : "=m" (*to) : "r" (x), "r" (to));
157 }
158
159 static inline void write_long_big(volatile ULONG *to, ULONG x)
160 {
161     asm volatile ("stw %1,%0\n eieio" : "=m" (*to) : "r" (x));
162 }
163
164 static int log_init = 0;
165 static int log_do = 0;
166 static int log_low = 0;
167
168 int dolog(int port)
169 {
170     if (log_init && log_do) 
171     {
172         if (log_low && port > 0x400) return 0;
173         return 1;
174     }
175
176     if (!log_init)
177     {
178         log_init = 1;
179         log_do = (getenv("x86_logio") != (char *)0);
180         log_low = (getenv("x86_loglow") != (char *)0);
181         if (log_do) 
182         {
183             if (log_low && port > 0x400) return 0;
184             return 1;
185         }
186     }
187     return 0;
188 }
189
190 // Converts an emulator address to a physical address.
191 // Handles all special cases (bios date, model etc), and might need work
192 u32 memaddr(u32 addr)
193 {
194 //    if (addr >= 0xF0000 && addr < 0xFFFFF) printf("WARNING: Segment F access (0x%x)\n", addr);
195 //    printf("MemAddr=%p\n", addr);
196     if (addr >= 0xA0000 && addr < 0xC0000)
197         return 0xFD000000 + addr;
198     else if (addr >= 0xFFFF5 && addr < 0xFFFFE)
199     {
200         return (u32)bios_date+addr-0xFFFF5;
201     }
202     else if (addr == 0xFFFFE)
203         return (u32)&model;
204     else if (addr == 0xFFFFF)
205         return (u32)&submodel;
206     else if (addr >= 0x80000000)
207     {
208         //printf("Warning: High memory access at 0x%x\n", addr);
209         return addr;
210     }
211     else
212         return (u32)M.mem_base+addr;
213 }
214
215 u8 A1_rdb(u32 addr)
216 {
217     u8 a = read_byte((UBYTE *)memaddr(addr));
218     LOGMEM("rdb: %x -> %x\n", addr, a);
219     return a;
220 }
221
222 u16 A1_rdw(u32 addr)
223 {
224     u16 a = read_word_little((UWORD *)memaddr(addr));
225     LOGMEM("rdw: %x -> %x\n", addr, a);
226     return a;
227 }
228
229 u32 A1_rdl(u32 addr)
230 {
231     u32 a = read_long_little((ULONG *)memaddr(addr));
232     LOGMEM("rdl: %x -> %x\n", addr, a);
233     return a;
234 }
235
236 void A1_wrb(u32 addr, u8 val)
237 {
238     LOGMEM("wrb: %x <- %x\n", addr, val);
239     write_byte((UBYTE *)memaddr(addr), val);
240 }
241
242 void A1_wrw(u32 addr, u16 val)
243 {
244     LOGMEM("wrw: %x <- %x\n", addr, val);
245     write_word_little((UWORD *)memaddr(addr), val);
246 }
247
248 void A1_wrl(u32 addr, u32 val)
249 {
250     LOGMEM("wrl: %x <- %x\n", addr, val);
251     write_long_little((ULONG *)memaddr(addr), val);
252 }
253
254 X86EMU_memFuncs _A1_mem = 
255 {
256     A1_rdb,
257     A1_rdw,
258     A1_rdl,
259     A1_wrb,
260     A1_wrw,
261     A1_wrl,
262 };
263
264 #define ARTICIAS_PCI_CFGADDR  0xfec00cf8
265 #define ARTICIAS_PCI_CFGDATA  0xfee00cfc
266 #define IOBASE                0xFE000000
267
268 #define in_byte(from) read_byte( (UBYTE *)port_to_mem(from))
269 #define in_word(from) read_word_little((UWORD *)port_to_mem(from))
270 #define in_long(from) read_long_little((ULONG *)port_to_mem(from))
271 #define out_byte(to, val) write_byte((UBYTE *)port_to_mem(to), val)
272 #define out_word(to, val) write_word_little((UWORD *)port_to_mem(to), val)
273 #define out_long(to, val) write_long_little((ULONG *)port_to_mem(to), val)
274
275 u32 port_to_mem(int port)
276 {
277     if (port >= 0xCFC && port <= 0xCFF) return 0xFEE00000+port;
278     else if (port >= 0xCF8 && port <= 0xCFB) return 0xFEC00000+port;
279     else return IOBASE + port;
280 }
281
282 u8 A1_inb(int port)
283 {
284     u8 a;
285     //if (port == 0x3BA) return 0;
286     a = in_byte(port);
287     LOGIO(port, "inb: %Xh -> %d (%Xh)\n", port, a, a);
288     return a;
289 }
290
291 u16 A1_inw(int port)
292 {
293     u16 a = in_word(port);
294     LOGIO(port, "inw: %Xh -> %d (%Xh)\n", port, a, a);
295     return a;
296 }
297
298 u32 A1_inl(int port)
299 {
300     u32 a = in_long(port);
301     LOGIO(port, "inl: %Xh -> %d (%Xh)\n", port, a, a);
302     return a;
303 }
304
305 void A1_outb(int port, u8 val)
306 {
307     LOGIO(port, "outb: %Xh <- %d (%Xh)\n", port, val, val);
308 /*    if (port == 0xCF8) port = 0xCFB;
309     else if (port == 0xCF9) port = 0xCFA;
310     else if (port == 0xCFA) port = 0xCF9;
311     else if (port == 0xCFB) port = 0xCF8;*/
312     out_byte(port, val);
313 }
314
315 void A1_outw(int port, u16 val)
316 {
317     LOGIO(port, "outw: %Xh <- %d (%Xh)\n", port, val, val);
318     out_word(port, val);
319 }
320
321 void A1_outl(int port, u32 val)
322 {
323     LOGIO(port, "outl: %Xh <- %d (%Xh)\n", port, val, val);
324     out_long(port, val);
325 }
326
327 X86EMU_pioFuncs _A1_pio = 
328 {
329     A1_inb,
330     A1_inw,
331     A1_inl,
332     A1_outb,
333     A1_outw,
334     A1_outl,
335 };
336
337 static int reloced_ops = 0;
338
339 void reloc_ops(void *reloc_addr)
340 {
341     extern void (*x86emu_optab[256])(u8);
342     extern void (*x86emu_optab2[256])(u8);
343     extern void tables_relocate(unsigned int offset);
344     int i;
345     unsigned long delta;
346     if (reloced_ops == 1) return;
347     reloced_ops = 1;
348
349     delta = TEXT_BASE - (unsigned long)reloc_addr;
350
351     for (i=0; i<256; i++)
352     {
353         x86emu_optab[i] -= delta;
354         x86emu_optab2[i] -= delta;
355     }
356     
357     _A1_mem.rdb = A1_rdb;
358     _A1_mem.rdw = A1_rdw;
359     _A1_mem.rdl = A1_rdl;
360     _A1_mem.wrb = A1_wrb;
361     _A1_mem.wrw = A1_wrw;
362     _A1_mem.wrl = A1_wrl;
363
364     _A1_pio.inb = A1_inb;
365     _A1_pio.inw = A1_inw;
366     _A1_pio.inl = A1_inl;
367     _A1_pio.outb = A1_outb;
368     _A1_pio.outw = A1_outw;
369     _A1_pio.outl = A1_outl;
370     
371     tables_relocate(delta);
372
373 }
374
375
376 #define ANY_KEY(text)                           \
377     printf(text);                               \
378     while (!tstc());
379
380
381 unsigned char more_strap[] = {
382         0xb4, 0x0, 0xb0, 0x2, 0xcd, 0x10,
383 };
384 #define MORE_STRAP_BYTES 6 // Additional bytes of strap code
385
386
387 unsigned char *done_msg="VGA Initialized\0";
388
389 int execute_bios(pci_dev_t gr_dev, void *reloc_addr)
390 {
391     extern void bios_init(void);
392     extern void remove_init_data(void);
393     extern int video_rows(void);
394     extern int video_cols(void);
395     extern int video_size(int, int);
396     u8 *strap;
397     unsigned char *logo;
398     u8 cfg;
399     int i;
400     char c;
401 #ifdef DEBUG
402     char *s;
403 #endif
404 #ifdef EASTEREGG
405     int easteregg_active = 0;
406 #endif
407     char *pal_reset;
408     u8 *fb;
409     unsigned char *msg;
410     unsigned char current_attr;
411
412     remove_init_data();
413     PRINTF("Removed init data from cache, now in RAM\n");
414
415     reloc_ops(reloc_addr);
416     PRINTF("Attempting to run emulator on %02x:%02x:%02x\n",
417            PCI_BUS(gr_dev), PCI_DEV(gr_dev), PCI_FUNC(gr_dev));
418
419     // Enable compatibility hole for emulator access to frame buffer
420     PRINTF("Enabling compatibility hole\n");
421     enable_compatibility_hole();
422
423     // Allocate memory
424     // FIXME: We shouldn't use this much memory really.
425     memset(&M, 0, sizeof(X86EMU_sysEnv));
426     M.mem_base = malloc(EMULATOR_MEM_SIZE);
427     M.mem_size = EMULATOR_MEM_SIZE;
428
429     if (!M.mem_base)
430     {
431         PRINTF("Unable to allocate one megabyte for emulator\n");
432         return 0;
433     }
434
435     if (attempt_map_rom(gr_dev, M.mem_base + EMULATOR_BIOS_OFFSET) == 0)
436     {
437         PRINTF("Error mapping rom. Emulation terminated\n");
438         return 0;
439     }
440
441 #ifdef DEBUG
442     s = getenv("x86_ask_start");
443     if (s)
444     {
445         printf("Press 'q' to skip initialization, 'd' for dry init\n'i' for i/o session");
446         while (!tstc());
447         c = getc();
448         if (c == 'q') return 0;
449         if (c == 'd')
450         {
451             extern void bios_set_mode(int mode);
452             bios_set_mode(0x03);
453             return 0;
454         }
455         if (c == 'i') do_inout();
456     }
457
458
459 #endif
460
461 #ifdef EASTEREGG
462 /*    if (tstc())
463     {
464         if (getc() == 'c')
465         {
466             easteregg_active = 1;
467         }
468     }
469 */
470     if (getenv("easteregg"))
471     {
472         easteregg_active = 1;
473     }
474     
475     if (easteregg_active)
476     {
477         // Yay!
478         setenv("x86_mode", "1");
479         setenv("vga_fg_color", "11");
480         setenv("vga_bg_color", "1");
481         easteregg_active = 1;
482     }
483 #endif
484
485     strap = (u8*)M.mem_base + EMULATOR_STRAP_OFFSET;
486
487     {
488         char *m = getenv("x86_mode");
489         if (m)
490         {
491             more_strap[3] = atoi(m);
492             if (more_strap[3] == 1) video_size(40, 25);
493             else                    video_size(80, 25);
494         }
495     }
496
497     /* 
498      * Poke the strap routine. This might need a bit of extending
499      * if there is a mode switch involved, i.e. we want to int10 
500      * afterwards to set a different graphics mode, or alternatively
501      * there might be a different start address requirement if the
502      * ROM doesn't have an x86 image in its first image.
503      */
504
505     PRINTF("Poking strap...\n");
506
507     // FAR CALL c000:0003
508     *strap++ = 0x9A; *strap++ = 0x03; *strap++ = 0x00;
509     *strap++ = 0x00; *strap++ = 0xC0; 
510
511 #if 1
512     // insert additional strap code
513     for (i=0; i < MORE_STRAP_BYTES; i++)
514     {
515         *strap++ = more_strap[i];
516     }
517 #endif
518     // HALT
519     *strap++ = 0xF4; 
520
521     PRINTF("Setting up logo data\n");
522     logo = (unsigned char *)M.mem_base + EMULATOR_LOGO_OFFSET;
523     for (i=0; i<16; i++)
524     {
525         *logo++ = 0xFF;
526     }
527
528     /*
529      * Setup the init parameters.
530      * Per PCI specs, AH must contain the bus and AL
531      * must contain the devfn, encoded as (dev<<3)|fn
532      */
533
534     // Execution starts here
535     M.x86.R_CS = SEG(EMULATOR_STRAP_OFFSET); 
536     M.x86.R_IP = OFF(EMULATOR_STRAP_OFFSET); 
537
538     // Stack at top of ram
539     M.x86.R_SS = SEG(EMULATOR_STACK_OFFSET);
540     M.x86.R_SP = OFF(EMULATOR_STACK_OFFSET);
541
542     // Input parameters
543     M.x86.R_AH = PCI_BUS(gr_dev);
544     M.x86.R_AL = (PCI_DEV(gr_dev)<<3) | PCI_FUNC(gr_dev);
545
546     // Set the I/O and memory access functions
547     X86EMU_setupMemFuncs(&_A1_mem);
548     X86EMU_setupPioFuncs(&_A1_pio);
549
550     // Enable timer 2
551     cfg = in_byte(0x61); // Get Misc control
552     cfg |= 0x01;         // Enable timer 2
553     out_byte(0x61, cfg); // output again
554
555     // Set up the timers
556     out_byte(0x43, 0x54);
557     out_byte(0x41, 0x18);
558
559     out_byte(0x43, 0x36);
560     out_byte(0x40, 0x00);
561     out_byte(0x40, 0x00);
562
563     out_byte(0x43, 0xb6);
564     out_byte(0x42, 0x31);
565     out_byte(0x42, 0x13);
566
567     // Init the "BIOS".
568     bios_init();
569
570     // Video Card Reset
571     out_byte(0x3D8, 0);
572     out_byte(0x3B8, 1);
573     (void)in_byte(0x3BA);
574     (void)in_byte(0x3DA);
575     out_byte(0x3C0, 0);
576     out_byte(0x61, 0xFC);
577
578 #ifdef DEBUG
579     s = _getenv("x86_singlestep");
580     if (s && strcmp(s, "on")==0)
581     {
582         PRINTF("Enabling single stepping for debug\n");
583         X86EMU_trace_on();
584     }
585 #endif
586
587     // Ready set go...
588     PRINTF("Running emulator\n");
589     X86EMU_exec();
590     PRINTF("Done running emulator\n");
591
592 /* FIXME: Remove me */
593     pal_reset = getenv("x86_palette_reset");
594     if (pal_reset && strcmp(pal_reset, "on") == 0)
595     {
596         PRINTF("Palette reset\n");
597         //(void)in_byte(0x3da);
598         //out_byte(0x3c0, 0);
599
600         out_byte(0x3C8, 0);
601         out_byte(0x3C9, 0);
602         out_byte(0x3C9, 0);
603         out_byte(0x3C9, 0);
604         for (i=0; i<254; i++)
605         {
606             out_byte(0x3C9, 63);
607             out_byte(0x3C9, 63);
608             out_byte(0x3C9, 63);
609         }
610
611         out_byte(0x3c0, 0x20);
612     }
613 /* FIXME: remove me */
614 #ifdef EASTEREGG
615     if (easteregg_active)
616     {
617         extern void video_easteregg(void);
618         video_easteregg();
619     }
620 #endif
621 /*
622     current_attr = video_get_attr();
623     fb = (u8 *)VIDEO_BASE;
624     for (i=0; i<video_rows()*video_cols()*2; i+=2)
625     {
626         *(fb+i) = ' ';
627         *(fb+i+1) = current_attr;
628     }
629
630     fb = (u8 *)VIDEO_BASE + (video_rows())-1*(video_cols()*2);
631     for (i=0; i<video_cols(); i++)
632     {
633         *(fb + 2*i)     = 32;
634         *(fb + 2*i + 1) = 0x17;
635     }
636
637     msg = done_msg;
638     while (*msg)
639     {
640         *fb = *msg;
641         fb  += 2;
642         msg ++;
643     }
644 */
645 #ifdef DEBUG
646     if (getenv("x86_do_inout")) do_inout();
647 #endif
648
649     dcache_disable();
650     return 1;
651 }
652
653 // Clean up the x86 mess
654 void shutdown_bios(void)
655 {
656 //    disable_compatibility_hole();
657     // Free the memory associated
658     free(M.mem_base);
659
660 }
661
662 int to_int(char *buffer)
663 {
664     int base = 0;
665     int res  = 0;
666
667     if (*buffer == '$') 
668     {
669         base = 16;
670         buffer++;
671     }
672     else base = 10;
673
674     for (;;)
675     {
676         switch(*buffer)
677         {
678         case '0' ... '9':
679             res *= base;
680             res += *buffer - '0';
681             break;
682         case 'A':
683         case 'a':
684             res *= base;
685             res += 10;
686             break;
687         case 'B':
688         case 'b':
689             res *= base;
690             res += 11;
691             break;          
692         case 'C':
693         case 'c':
694             res *= base;
695             res += 12;
696             break;          
697         case 'D':
698         case 'd':
699             res *= base;
700             res += 13;
701             break;          
702         case 'E':
703         case 'e':
704             res *= base;
705             res += 14;
706             break;          
707         case 'F':
708         case 'f':
709             res *= base;
710             res += 15;
711             break;      
712         default:
713             return res;
714         }
715         buffer++;
716     }
717     return res;
718 }
719
720 void one_arg(char *buffer, int *a)
721 {
722     while (*buffer && *buffer != '\n')
723     {
724         if (*buffer == ' ') buffer++;
725         else break;
726     }
727
728     *a = to_int(buffer);
729 }
730
731 void two_args(char *buffer, int *a, int *b)
732 {
733     while (*buffer && *buffer != '\n')
734     {
735         if (*buffer == ' ') buffer++;
736         else break;
737     }
738
739     *a = to_int(buffer);
740
741     while (*buffer && *buffer != '\n')
742     {
743         if (*buffer != ' ') buffer++;
744         else break;
745     }
746
747     while (*buffer && *buffer != '\n')
748     {
749         if (*buffer == ' ') buffer++;
750         else break;
751     }
752
753     *b = to_int(buffer);
754 }
755
756 void do_inout(void)
757 {
758     char buffer[256];
759     char *arg1, *arg2;
760     int a,b;
761
762     printf("In/Out Session\nUse 'i[bwl]' for in, 'o[bwl]' for out and 'q' to quit\n");
763
764     do
765     {
766         cons_gets(buffer);
767         printf("\n");
768
769         *arg1 = buffer;
770         while (*arg1 != ' ' ) arg1++;
771         while (*arg1 == ' ') arg1++;
772
773         if (buffer[0] == 'i')
774         {
775             one_arg(buffer+2, &a);
776             switch (buffer[1])
777             {
778             case 'b':
779                 printf("in_byte(%xh) = %xh\n", a, A1_inb(a));
780                 break;
781             case 'w':
782                 printf("in_word(%xh) = %xh\n", a, A1_inw(a));
783                 break;
784             case 'l':
785                 printf("in_dword(%xh) = %xh\n", a, A1_inl(a));
786                 break;
787             default:
788                 printf("Invalid length '%c'\n", buffer[1]);
789                 break;
790             }
791         }
792         else if (buffer[0] == 'o')
793         {
794             two_args(buffer+2, &a, &b);
795             switch (buffer[1])
796             {
797             case 'b':
798                 printf("out_byte(%d, %d)\n", a, b);
799                 A1_outb(a,b);
800                 break;
801             case 'w':
802                 printf("out_word(%d, %d)\n", a, b);
803                 A1_outw(a, b);
804                 break;
805             case 'l':
806                 printf("out_long(%d, %d)\n", a, b);
807                 A1_outl(a, b);
808                 break;
809             default:
810                 printf("Invalid length '%c'\n", buffer[1]);
811                 break;
812             }
813         } else if (buffer[0] == 'q') return;
814     } while (1);
815 }