]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - board/MAI/bios_emulator/x86interface.c
* Patch by Hans-Joerg Frieden, 06 Dec 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     char *s;
402 #ifdef EASTEREGG
403     int easteregg_active = 0;
404 #endif
405     char *pal_reset;
406     u8 *fb;
407     unsigned char *msg;
408     unsigned char current_attr;
409
410     PRINTF("Trying to remove init data\n");
411     remove_init_data();
412     PRINTF("Removed init data from cache, now in RAM\n");
413
414     reloc_ops(reloc_addr);
415     PRINTF("Attempting to run emulator on %02x:%02x:%02x\n",
416            PCI_BUS(gr_dev), PCI_DEV(gr_dev), PCI_FUNC(gr_dev));
417
418     // Enable compatibility hole for emulator access to frame buffer
419     PRINTF("Enabling compatibility hole\n");
420     enable_compatibility_hole();
421
422     // Allocate memory
423     // FIXME: We shouldn't use this much memory really.
424     memset(&M, 0, sizeof(X86EMU_sysEnv));
425     M.mem_base = malloc(EMULATOR_MEM_SIZE);
426     M.mem_size = EMULATOR_MEM_SIZE;
427
428     if (!M.mem_base)
429     {
430         PRINTF("Unable to allocate one megabyte for emulator\n");
431         return 0;
432     }
433
434     if (attempt_map_rom(gr_dev, M.mem_base + EMULATOR_BIOS_OFFSET) == 0)
435     {
436         PRINTF("Error mapping rom. Emulation terminated\n");
437         return 0;
438     }
439
440 #if 1 /*def DEBUG*/
441     s = getenv("x86_ask_start");
442     if (s)
443     {
444         printf("Press 'q' to skip initialization, 'd' for dry init\n'i' for i/o session");
445         while (!tstc());
446         c = getc();
447         if (c == 'q') return 0;
448         if (c == 'd')
449         {
450             extern void bios_set_mode(int mode);
451             bios_set_mode(0x03);
452             return 0;
453         }
454         if (c == 'i') do_inout();
455     }
456
457
458 #endif
459
460 #ifdef EASTEREGG
461 /*    if (tstc())
462     {
463         if (getc() == 'c')
464         {
465             easteregg_active = 1;
466         }
467     }
468 */
469     if (getenv("easteregg"))
470     {
471         easteregg_active = 1;
472     }
473     
474     if (easteregg_active)
475     {
476         // Yay!
477         setenv("x86_mode", "1");
478         setenv("vga_fg_color", "11");
479         setenv("vga_bg_color", "1");
480         easteregg_active = 1;
481     }
482 #endif
483
484     strap = (u8*)M.mem_base + EMULATOR_STRAP_OFFSET;
485
486     {
487         char *m = getenv("x86_mode");
488         if (m)
489         {
490             more_strap[3] = atoi(m);
491             if (more_strap[3] == 1) video_size(40, 25);
492             else                    video_size(80, 25);
493         }
494     }
495
496     /* 
497      * Poke the strap routine. This might need a bit of extending
498      * if there is a mode switch involved, i.e. we want to int10 
499      * afterwards to set a different graphics mode, or alternatively
500      * there might be a different start address requirement if the
501      * ROM doesn't have an x86 image in its first image.
502      */
503
504     PRINTF("Poking strap...\n");
505
506     // FAR CALL c000:0003
507     *strap++ = 0x9A; *strap++ = 0x03; *strap++ = 0x00;
508     *strap++ = 0x00; *strap++ = 0xC0; 
509
510 #if 1
511     // insert additional strap code
512     for (i=0; i < MORE_STRAP_BYTES; i++)
513     {
514         *strap++ = more_strap[i];
515     }
516 #endif
517     // HALT
518     *strap++ = 0xF4; 
519
520     PRINTF("Setting up logo data\n");
521     logo = (unsigned char *)M.mem_base + EMULATOR_LOGO_OFFSET;
522     for (i=0; i<16; i++)
523     {
524         *logo++ = 0xFF;
525     }
526
527     /*
528      * Setup the init parameters.
529      * Per PCI specs, AH must contain the bus and AL
530      * must contain the devfn, encoded as (dev<<3)|fn
531      */
532
533     // Execution starts here
534     M.x86.R_CS = SEG(EMULATOR_STRAP_OFFSET); 
535     M.x86.R_IP = OFF(EMULATOR_STRAP_OFFSET); 
536
537     // Stack at top of ram
538     M.x86.R_SS = SEG(EMULATOR_STACK_OFFSET);
539     M.x86.R_SP = OFF(EMULATOR_STACK_OFFSET);
540
541     // Input parameters
542     M.x86.R_AH = PCI_BUS(gr_dev);
543     M.x86.R_AL = (PCI_DEV(gr_dev)<<3) | PCI_FUNC(gr_dev);
544
545     // Set the I/O and memory access functions
546     X86EMU_setupMemFuncs(&_A1_mem);
547     X86EMU_setupPioFuncs(&_A1_pio);
548
549     // Enable timer 2
550     cfg = in_byte(0x61); // Get Misc control
551     cfg |= 0x01;         // Enable timer 2
552     out_byte(0x61, cfg); // output again
553
554     // Set up the timers
555     out_byte(0x43, 0x54);
556     out_byte(0x41, 0x18);
557
558     out_byte(0x43, 0x36);
559     out_byte(0x40, 0x00);
560     out_byte(0x40, 0x00);
561
562     out_byte(0x43, 0xb6);
563     out_byte(0x42, 0x31);
564     out_byte(0x42, 0x13);
565
566     // Init the "BIOS".
567     bios_init();
568
569     // Video Card Reset
570     out_byte(0x3D8, 0);
571     out_byte(0x3B8, 1);
572     (void)in_byte(0x3BA);
573     (void)in_byte(0x3DA);
574     out_byte(0x3C0, 0);
575     out_byte(0x61, 0xFC);
576
577 #ifdef DEBUG
578     s = _getenv("x86_singlestep");
579     if (s && strcmp(s, "on")==0)
580     {
581         PRINTF("Enabling single stepping for debug\n");
582         X86EMU_trace_on();
583     }
584 #endif
585
586     // Ready set go...
587     PRINTF("Running emulator\n");
588     X86EMU_exec();
589     PRINTF("Done running emulator\n");
590
591 /* FIXME: Remove me */
592     pal_reset = getenv("x86_palette_reset");
593     if (pal_reset && strcmp(pal_reset, "on") == 0)
594     {
595         PRINTF("Palette reset\n");
596         //(void)in_byte(0x3da);
597         //out_byte(0x3c0, 0);
598
599         out_byte(0x3C8, 0);
600         out_byte(0x3C9, 0);
601         out_byte(0x3C9, 0);
602         out_byte(0x3C9, 0);
603         for (i=0; i<254; i++)
604         {
605             out_byte(0x3C9, 63);
606             out_byte(0x3C9, 63);
607             out_byte(0x3C9, 63);
608         }
609
610         out_byte(0x3c0, 0x20);
611     }
612 /* FIXME: remove me */
613 #ifdef EASTEREGG
614     if (easteregg_active)
615     {
616         extern void video_easteregg(void);
617         video_easteregg();
618     }
619 #endif
620 /*
621     current_attr = video_get_attr();
622     fb = (u8 *)VIDEO_BASE;
623     for (i=0; i<video_rows()*video_cols()*2; i+=2)
624     {
625         *(fb+i) = ' ';
626         *(fb+i+1) = current_attr;
627     }
628
629     fb = (u8 *)VIDEO_BASE + (video_rows())-1*(video_cols()*2);
630     for (i=0; i<video_cols(); i++)
631     {
632         *(fb + 2*i)     = 32;
633         *(fb + 2*i + 1) = 0x17;
634     }
635
636     msg = done_msg;
637     while (*msg)
638     {
639         *fb = *msg;
640         fb  += 2;
641         msg ++;
642     }
643 */
644 #ifdef DEBUG
645     if (getenv("x86_do_inout")) do_inout();
646 #endif
647
648 //FIXME:    dcache_disable();
649     return 1;
650 }
651
652 // Clean up the x86 mess
653 void shutdown_bios(void)
654 {
655 //    disable_compatibility_hole();
656     // Free the memory associated
657     free(M.mem_base);
658
659 }
660
661 int to_int(char *buffer)
662 {
663     int base = 0;
664     int res  = 0;
665
666     if (*buffer == '$') 
667     {
668         base = 16;
669         buffer++;
670     }
671     else base = 10;
672
673     for (;;)
674     {
675         switch(*buffer)
676         {
677         case '0' ... '9':
678             res *= base;
679             res += *buffer - '0';
680             break;
681         case 'A':
682         case 'a':
683             res *= base;
684             res += 10;
685             break;
686         case 'B':
687         case 'b':
688             res *= base;
689             res += 11;
690             break;          
691         case 'C':
692         case 'c':
693             res *= base;
694             res += 12;
695             break;          
696         case 'D':
697         case 'd':
698             res *= base;
699             res += 13;
700             break;          
701         case 'E':
702         case 'e':
703             res *= base;
704             res += 14;
705             break;          
706         case 'F':
707         case 'f':
708             res *= base;
709             res += 15;
710             break;      
711         default:
712             return res;
713         }
714         buffer++;
715     }
716     return res;
717 }
718
719 void one_arg(char *buffer, int *a)
720 {
721     while (*buffer && *buffer != '\n')
722     {
723         if (*buffer == ' ') buffer++;
724         else break;
725     }
726
727     *a = to_int(buffer);
728 }
729
730 void two_args(char *buffer, int *a, int *b)
731 {
732     while (*buffer && *buffer != '\n')
733     {
734         if (*buffer == ' ') buffer++;
735         else break;
736     }
737
738     *a = to_int(buffer);
739
740     while (*buffer && *buffer != '\n')
741     {
742         if (*buffer != ' ') buffer++;
743         else break;
744     }
745
746     while (*buffer && *buffer != '\n')
747     {
748         if (*buffer == ' ') buffer++;
749         else break;
750     }
751
752     *b = to_int(buffer);
753 }
754
755 void do_inout(void)
756 {
757     char buffer[256];
758     char *arg1, *arg2;
759     int a,b;
760
761     printf("In/Out Session\nUse 'i[bwl]' for in, 'o[bwl]' for out and 'q' to quit\n");
762
763     do
764     {
765         cons_gets(buffer);
766         printf("\n");
767
768         *arg1 = buffer;
769         while (*arg1 != ' ' ) arg1++;
770         while (*arg1 == ' ') arg1++;
771
772         if (buffer[0] == 'i')
773         {
774             one_arg(buffer+2, &a);
775             switch (buffer[1])
776             {
777             case 'b':
778                 printf("in_byte(%xh) = %xh\n", a, A1_inb(a));
779                 break;
780             case 'w':
781                 printf("in_word(%xh) = %xh\n", a, A1_inw(a));
782                 break;
783             case 'l':
784                 printf("in_dword(%xh) = %xh\n", a, A1_inl(a));
785                 break;
786             default:
787                 printf("Invalid length '%c'\n", buffer[1]);
788                 break;
789             }
790         }
791         else if (buffer[0] == 'o')
792         {
793             two_args(buffer+2, &a, &b);
794             switch (buffer[1])
795             {
796             case 'b':
797                 printf("out_byte(%d, %d)\n", a, b);
798                 A1_outb(a,b);
799                 break;
800             case 'w':
801                 printf("out_word(%d, %d)\n", a, b);
802                 A1_outw(a, b);
803                 break;
804             case 'l':
805                 printf("out_long(%d, %d)\n", a, b);
806                 A1_outl(a, b);
807                 break;
808             default:
809                 printf("Invalid length '%c'\n", buffer[1]);
810                 break;
811             }
812         } else if (buffer[0] == 'q') return;
813     } while (1);
814 }