summaryrefslogtreecommitdiffstats
path: root/board/MAI/bios_emulator/x86interface.c
diff options
context:
space:
mode:
Diffstat (limited to 'board/MAI/bios_emulator/x86interface.c')
-rw-r--r--board/MAI/bios_emulator/x86interface.c815
1 files changed, 815 insertions, 0 deletions
diff --git a/board/MAI/bios_emulator/x86interface.c b/board/MAI/bios_emulator/x86interface.c
new file mode 100644
index 0000000000..b1ad61a6a0
--- /dev/null
+++ b/board/MAI/bios_emulator/x86interface.c
@@ -0,0 +1,815 @@
+#include "x86emu.h"
+#include "glue.h"
+
+
+/*
+ * This isn't nice, but there are a lot of incompatibilities in the U-Boot and scitech include
+ * files that this is the only really workable solution.
+ * Might be cleaned out later.
+ */
+
+#ifdef DEBUG
+#undef DEBUG
+#endif
+
+#undef IO_LOGGING
+#undef MEM_LOGGING
+
+#ifdef IO_LOGGING
+#define LOGIO(port, format, args...) if (dolog(port)) _printf(format , ## args)
+#else
+#define LOGIO(port, format, args...)
+#endif
+
+#ifdef MEM_LOGGIN
+#define LOGMEM(format, args...) _printf(format , ## args)
+#else
+#define LOGMEM(format, args...)
+#endif
+
+#ifdef DEBUG
+#define PRINTF(format, args...) _printf(format , ## args)
+#else
+#define PRINTF(format, argc...)
+#endif
+
+typedef unsigned char UBYTE;
+typedef unsigned short UWORD;
+typedef unsigned long ULONG;
+
+typedef char BYTE;
+typedef short WORT;
+typedef long LONG;
+
+#define EMULATOR_MEM_SIZE (1024*1024)
+#define EMULATOR_BIOS_OFFSET 0xC0000
+#define EMULATOR_STRAP_OFFSET 0x30000
+#define EMULATOR_STACK_OFFSET 0x20000
+#define EMULATOR_LOGO_OFFSET 0x40000 // If you change this, change the strap code, too
+#define VIDEO_BASE (void *)0xFD0B8000
+
+extern char *getenv(char *);
+extern int tstc(void);
+extern int getc(void);
+extern unsigned char video_get_attr(void);
+
+int atoi(char *string)
+{
+ int res = 0;
+ while (*string>='0' && *string <='9')
+ {
+ res *= 10;
+ res += *string-'0';
+ string++;
+ }
+
+ return res;
+}
+
+void cons_gets(char *buffer)
+{
+ int i = 0;
+ char c = 0;
+
+ buffer[0] = 0;
+ if (getenv("x86_runthru")) return; //FIXME:
+ while (c != 0x0D && c != 0x0A)
+ {
+ while (!tstc());
+ c = getc();
+ if (c>=32 && c < 127)
+ {
+ buffer[i] = c;
+ i++;
+ buffer[i] = 0;
+ putc(c);
+ }
+ else
+ {
+ if (c == 0x08)
+ {
+ if (i>0) i--;
+ buffer[i] = 0;
+ }
+ }
+ }
+ buffer[i] = '\n';
+ buffer[i+1] = 0;
+}
+
+char *bios_date = "08/14/02";
+UBYTE model = 0xFC;
+UBYTE submodel = 0x00;
+
+static inline UBYTE read_byte(volatile UBYTE* from)
+{
+ int x;
+ asm volatile ("lbz %0,%1\n eieio" : "=r" (x) : "m" (*from));
+ return (UBYTE)x;
+}
+
+static inline void write_byte(volatile UBYTE *to, int x)
+{
+ asm volatile ("stb %1,%0\n eieio" : "=m" (*to) : "r" (x));
+}
+
+static inline UWORD read_word_little(volatile UWORD *from)
+{
+ int x;
+ asm volatile ("lhbrx %0,0,%1\n eieio" : "=r" (x) : "r" (from), "m" (*from));
+ return (UWORD)x;
+}
+
+static inline UWORD read_word_big(volatile UWORD *from)
+{
+ int x;
+ asm volatile ("lhz %0,%1\n eieio" : "=r" (x) : "m" (*from));
+ return (UWORD)x;
+}
+
+static inline void write_word_little(volatile UWORD *to, int x)
+{
+ asm volatile ("sthbrx %1,0,%2\n eieio" : "=m" (*to) : "r" (x), "r" (to));
+}
+
+static inline void write_word_big(volatile UWORD *to, int x)
+{
+ asm volatile ("sth %1,%0\n eieio" : "=m" (*to) : "r" (x));
+}
+
+static inline ULONG read_long_little(volatile ULONG *from)
+{
+ unsigned long x;
+ asm volatile ("lwbrx %0,0,%1\n eieio" : "=r" (x) : "r" (from), "m"(*from));
+ return (ULONG)x;
+}
+
+static inline ULONG read_long_big(volatile ULONG *from)
+{
+ unsigned long x;
+ asm volatile ("lwz %0,%1\n eieio" : "=r" (x) : "m" (*from));
+ return (ULONG)x;
+}
+
+static inline void write_long_little(volatile ULONG *to, ULONG x)
+{
+ asm volatile ("stwbrx %1,0,%2\n eieio" : "=m" (*to) : "r" (x), "r" (to));
+}
+
+static inline void write_long_big(volatile ULONG *to, ULONG x)
+{
+ asm volatile ("stw %1,%0\n eieio" : "=m" (*to) : "r" (x));
+}
+
+static int log_init = 0;
+static int log_do = 0;
+static int log_low = 0;
+
+int dolog(int port)
+{
+ if (log_init && log_do)
+ {
+ if (log_low && port > 0x400) return 0;
+ return 1;
+ }
+
+ if (!log_init)
+ {
+ log_init = 1;
+ log_do = (getenv("x86_logio") != (char *)0);
+ log_low = (getenv("x86_loglow") != (char *)0);
+ if (log_do)
+ {
+ if (log_low && port > 0x400) return 0;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+// Converts an emulator address to a physical address.
+// Handles all special cases (bios date, model etc), and might need work
+u32 memaddr(u32 addr)
+{
+// if (addr >= 0xF0000 && addr < 0xFFFFF) printf("WARNING: Segment F access (0x%x)\n", addr);
+// printf("MemAddr=%p\n", addr);
+ if (addr >= 0xA0000 && addr < 0xC0000)
+ return 0xFD000000 + addr;
+ else if (addr >= 0xFFFF5 && addr < 0xFFFFE)
+ {
+ return (u32)bios_date+addr-0xFFFF5;
+ }
+ else if (addr == 0xFFFFE)
+ return (u32)&model;
+ else if (addr == 0xFFFFF)
+ return (u32)&submodel;
+ else if (addr >= 0x80000000)
+ {
+ //printf("Warning: High memory access at 0x%x\n", addr);
+ return addr;
+ }
+ else
+ return (u32)M.mem_base+addr;
+}
+
+u8 A1_rdb(u32 addr)
+{
+ u8 a = read_byte((UBYTE *)memaddr(addr));
+ LOGMEM("rdb: %x -> %x\n", addr, a);
+ return a;
+}
+
+u16 A1_rdw(u32 addr)
+{
+ u16 a = read_word_little((UWORD *)memaddr(addr));
+ LOGMEM("rdw: %x -> %x\n", addr, a);
+ return a;
+}
+
+u32 A1_rdl(u32 addr)
+{
+ u32 a = read_long_little((ULONG *)memaddr(addr));
+ LOGMEM("rdl: %x -> %x\n", addr, a);
+ return a;
+}
+
+void A1_wrb(u32 addr, u8 val)
+{
+ LOGMEM("wrb: %x <- %x\n", addr, val);
+ write_byte((UBYTE *)memaddr(addr), val);
+}
+
+void A1_wrw(u32 addr, u16 val)
+{
+ LOGMEM("wrw: %x <- %x\n", addr, val);
+ write_word_little((UWORD *)memaddr(addr), val);
+}
+
+void A1_wrl(u32 addr, u32 val)
+{
+ LOGMEM("wrl: %x <- %x\n", addr, val);
+ write_long_little((ULONG *)memaddr(addr), val);
+}
+
+X86EMU_memFuncs _A1_mem =
+{
+ A1_rdb,
+ A1_rdw,
+ A1_rdl,
+ A1_wrb,
+ A1_wrw,
+ A1_wrl,
+};
+
+#define ARTICIAS_PCI_CFGADDR 0xfec00cf8
+#define ARTICIAS_PCI_CFGDATA 0xfee00cfc
+#define IOBASE 0xFE000000
+
+#define in_byte(from) read_byte( (UBYTE *)port_to_mem(from))
+#define in_word(from) read_word_little((UWORD *)port_to_mem(from))
+#define in_long(from) read_long_little((ULONG *)port_to_mem(from))
+#define out_byte(to, val) write_byte((UBYTE *)port_to_mem(to), val)
+#define out_word(to, val) write_word_little((UWORD *)port_to_mem(to), val)
+#define out_long(to, val) write_long_little((ULONG *)port_to_mem(to), val)
+
+u32 port_to_mem(int port)
+{
+ if (port >= 0xCFC && port <= 0xCFF) return 0xFEE00000+port;
+ else if (port >= 0xCF8 && port <= 0xCFB) return 0xFEC00000+port;
+ else return IOBASE + port;
+}
+
+u8 A1_inb(int port)
+{
+ u8 a;
+ //if (port == 0x3BA) return 0;
+ a = in_byte(port);
+ LOGIO(port, "inb: %Xh -> %d (%Xh)\n", port, a, a);
+ return a;
+}
+
+u16 A1_inw(int port)
+{
+ u16 a = in_word(port);
+ LOGIO(port, "inw: %Xh -> %d (%Xh)\n", port, a, a);
+ return a;
+}
+
+u32 A1_inl(int port)
+{
+ u32 a = in_long(port);
+ LOGIO(port, "inl: %Xh -> %d (%Xh)\n", port, a, a);
+ return a;
+}
+
+void A1_outb(int port, u8 val)
+{
+ LOGIO(port, "outb: %Xh <- %d (%Xh)\n", port, val, val);
+/* if (port == 0xCF8) port = 0xCFB;
+ else if (port == 0xCF9) port = 0xCFA;
+ else if (port == 0xCFA) port = 0xCF9;
+ else if (port == 0xCFB) port = 0xCF8;*/
+ out_byte(port, val);
+}
+
+void A1_outw(int port, u16 val)
+{
+ LOGIO(port, "outw: %Xh <- %d (%Xh)\n", port, val, val);
+ out_word(port, val);
+}
+
+void A1_outl(int port, u32 val)
+{
+ LOGIO(port, "outl: %Xh <- %d (%Xh)\n", port, val, val);
+ out_long(port, val);
+}
+
+X86EMU_pioFuncs _A1_pio =
+{
+ A1_inb,
+ A1_inw,
+ A1_inl,
+ A1_outb,
+ A1_outw,
+ A1_outl,
+};
+
+static int reloced_ops = 0;
+
+void reloc_ops(void *reloc_addr)
+{
+ extern void (*x86emu_optab[256])(u8);
+ extern void (*x86emu_optab2[256])(u8);
+ extern void tables_relocate(unsigned int offset);
+ int i;
+ unsigned long delta;
+ if (reloced_ops == 1) return;
+ reloced_ops = 1;
+
+ delta = TEXT_BASE - (unsigned long)reloc_addr;
+
+ for (i=0; i<256; i++)
+ {
+ x86emu_optab[i] -= delta;
+ x86emu_optab2[i] -= delta;
+ }
+
+ _A1_mem.rdb = A1_rdb;
+ _A1_mem.rdw = A1_rdw;
+ _A1_mem.rdl = A1_rdl;
+ _A1_mem.wrb = A1_wrb;
+ _A1_mem.wrw = A1_wrw;
+ _A1_mem.wrl = A1_wrl;
+
+ _A1_pio.inb = A1_inb;
+ _A1_pio.inw = A1_inw;
+ _A1_pio.inl = A1_inl;
+ _A1_pio.outb = A1_outb;
+ _A1_pio.outw = A1_outw;
+ _A1_pio.outl = A1_outl;
+
+ tables_relocate(delta);
+
+}
+
+
+#define ANY_KEY(text) \
+ printf(text); \
+ while (!tstc());
+
+
+unsigned char more_strap[] = {
+ 0xb4, 0x0, 0xb0, 0x2, 0xcd, 0x10,
+};
+#define MORE_STRAP_BYTES 6 // Additional bytes of strap code
+
+
+unsigned char *done_msg="VGA Initialized\0";
+
+int execute_bios(pci_dev_t gr_dev, void *reloc_addr)
+{
+ extern void bios_init(void);
+ extern void remove_init_data(void);
+ extern int video_rows(void);
+ extern int video_cols(void);
+ extern int video_size(int, int);
+ u8 *strap;
+ unsigned char *logo;
+ u8 cfg;
+ int i;
+ char c;
+#ifdef DEBUG
+ char *s;
+#endif
+#ifdef EASTEREGG
+ int easteregg_active = 0;
+#endif
+ char *pal_reset;
+ u8 *fb;
+ unsigned char *msg;
+ unsigned char current_attr;
+
+ remove_init_data();
+ PRINTF("Removed init data from cache, now in RAM\n");
+
+ reloc_ops(reloc_addr);
+ PRINTF("Attempting to run emulator on %02x:%02x:%02x\n",
+ PCI_BUS(gr_dev), PCI_DEV(gr_dev), PCI_FUNC(gr_dev));
+
+ // Enable compatibility hole for emulator access to frame buffer
+ PRINTF("Enabling compatibility hole\n");
+ enable_compatibility_hole();
+
+ // Allocate memory
+ // FIXME: We shouldn't use this much memory really.
+ memset(&M, 0, sizeof(X86EMU_sysEnv));
+ M.mem_base = malloc(EMULATOR_MEM_SIZE);
+ M.mem_size = EMULATOR_MEM_SIZE;
+
+ if (!M.mem_base)
+ {
+ PRINTF("Unable to allocate one megabyte for emulator\n");
+ return 0;
+ }
+
+ if (attempt_map_rom(gr_dev, M.mem_base + EMULATOR_BIOS_OFFSET) == 0)
+ {
+ PRINTF("Error mapping rom. Emulation terminated\n");
+ return 0;
+ }
+
+#ifdef DEBUG
+ s = getenv("x86_ask_start");
+ if (s)
+ {
+ printf("Press 'q' to skip initialization, 'd' for dry init\n'i' for i/o session");
+ while (!tstc());
+ c = getc();
+ if (c == 'q') return 0;
+ if (c == 'd')
+ {
+ extern void bios_set_mode(int mode);
+ bios_set_mode(0x03);
+ return 0;
+ }
+ if (c == 'i') do_inout();
+ }
+
+
+#endif
+
+#ifdef EASTEREGG
+/* if (tstc())
+ {
+ if (getc() == 'c')
+ {
+ easteregg_active = 1;
+ }
+ }
+*/
+ if (getenv("easteregg"))
+ {
+ easteregg_active = 1;
+ }
+
+ if (easteregg_active)
+ {
+ // Yay!
+ setenv("x86_mode", "1");
+ setenv("vga_fg_color", "11");
+ setenv("vga_bg_color", "1");
+ easteregg_active = 1;
+ }
+#endif
+
+ strap = (u8*)M.mem_base + EMULATOR_STRAP_OFFSET;
+
+ {
+ char *m = getenv("x86_mode");
+ if (m)
+ {
+ more_strap[3] = atoi(m);
+ if (more_strap[3] == 1) video_size(40, 25);
+ else video_size(80, 25);
+ }
+ }
+
+ /*
+ * Poke the strap routine. This might need a bit of extending
+ * if there is a mode switch involved, i.e. we want to int10
+ * afterwards to set a different graphics mode, or alternatively
+ * there might be a different start address requirement if the
+ * ROM doesn't have an x86 image in its first image.
+ */
+
+ PRINTF("Poking strap...\n");
+
+ // FAR CALL c000:0003
+ *strap++ = 0x9A; *strap++ = 0x03; *strap++ = 0x00;
+ *strap++ = 0x00; *strap++ = 0xC0;
+
+#if 1
+ // insert additional strap code
+ for (i=0; i < MORE_STRAP_BYTES; i++)
+ {
+ *strap++ = more_strap[i];
+ }
+#endif
+ // HALT
+ *strap++ = 0xF4;
+
+ PRINTF("Setting up logo data\n");
+ logo = (unsigned char *)M.mem_base + EMULATOR_LOGO_OFFSET;
+ for (i=0; i<16; i++)
+ {
+ *logo++ = 0xFF;
+ }
+
+ /*
+ * Setup the init parameters.
+ * Per PCI specs, AH must contain the bus and AL
+ * must contain the devfn, encoded as (dev<<3)|fn
+ */
+
+ // Execution starts here
+ M.x86.R_CS = SEG(EMULATOR_STRAP_OFFSET);
+ M.x86.R_IP = OFF(EMULATOR_STRAP_OFFSET);
+
+ // Stack at top of ram
+ M.x86.R_SS = SEG(EMULATOR_STACK_OFFSET);
+ M.x86.R_SP = OFF(EMULATOR_STACK_OFFSET);
+
+ // Input parameters
+ M.x86.R_AH = PCI_BUS(gr_dev);
+ M.x86.R_AL = (PCI_DEV(gr_dev)<<3) | PCI_FUNC(gr_dev);
+
+ // Set the I/O and memory access functions
+ X86EMU_setupMemFuncs(&_A1_mem);
+ X86EMU_setupPioFuncs(&_A1_pio);
+
+ // Enable timer 2
+ cfg = in_byte(0x61); // Get Misc control
+ cfg |= 0x01; // Enable timer 2
+ out_byte(0x61, cfg); // output again
+
+ // Set up the timers
+ out_byte(0x43, 0x54);
+ out_byte(0x41, 0x18);
+
+ out_byte(0x43, 0x36);
+ out_byte(0x40, 0x00);
+ out_byte(0x40, 0x00);
+
+ out_byte(0x43, 0xb6);
+ out_byte(0x42, 0x31);
+ out_byte(0x42, 0x13);
+
+ // Init the "BIOS".
+ bios_init();
+
+ // Video Card Reset
+ out_byte(0x3D8, 0);
+ out_byte(0x3B8, 1);
+ (void)in_byte(0x3BA);
+ (void)in_byte(0x3DA);
+ out_byte(0x3C0, 0);
+ out_byte(0x61, 0xFC);
+
+#ifdef DEBUG
+ s = _getenv("x86_singlestep");
+ if (s && strcmp(s, "on")==0)
+ {
+ PRINTF("Enabling single stepping for debug\n");
+ X86EMU_trace_on();
+ }
+#endif
+
+ // Ready set go...
+ PRINTF("Running emulator\n");
+ X86EMU_exec();
+ PRINTF("Done running emulator\n");
+
+/* FIXME: Remove me */
+ pal_reset = getenv("x86_palette_reset");
+ if (pal_reset && strcmp(pal_reset, "on") == 0)
+ {
+ PRINTF("Palette reset\n");
+ //(void)in_byte(0x3da);
+ //out_byte(0x3c0, 0);
+
+ out_byte(0x3C8, 0);
+ out_byte(0x3C9, 0);
+ out_byte(0x3C9, 0);
+ out_byte(0x3C9, 0);
+ for (i=0; i<254; i++)
+ {
+ out_byte(0x3C9, 63);
+ out_byte(0x3C9, 63);
+ out_byte(0x3C9, 63);
+ }
+
+ out_byte(0x3c0, 0x20);
+ }
+/* FIXME: remove me */
+#ifdef EASTEREGG
+ if (easteregg_active)
+ {
+ extern void video_easteregg(void);
+ video_easteregg();
+ }
+#endif
+/*
+ current_attr = video_get_attr();
+ fb = (u8 *)VIDEO_BASE;
+ for (i=0; i<video_rows()*video_cols()*2; i+=2)
+ {
+ *(fb+i) = ' ';
+ *(fb+i+1) = current_attr;
+ }
+
+ fb = (u8 *)VIDEO_BASE + (video_rows())-1*(video_cols()*2);
+ for (i=0; i<video_cols(); i++)
+ {
+ *(fb + 2*i) = 32;
+ *(fb + 2*i + 1) = 0x17;
+ }
+
+ msg = done_msg;
+ while (*msg)
+ {
+ *fb = *msg;
+ fb += 2;
+ msg ++;
+ }
+*/
+#ifdef DEBUG
+ if (getenv("x86_do_inout")) do_inout();
+#endif
+
+ dcache_disable();
+ return 1;
+}
+
+// Clean up the x86 mess
+void shutdown_bios(void)
+{
+// disable_compatibility_hole();
+ // Free the memory associated
+ free(M.mem_base);
+
+}
+
+int to_int(char *buffer)
+{
+ int base = 0;
+ int res = 0;
+
+ if (*buffer == '$')
+ {
+ base = 16;
+ buffer++;
+ }
+ else base = 10;
+
+ for (;;)
+ {
+ switch(*buffer)
+ {
+ case '0' ... '9':
+ res *= base;
+ res += *buffer - '0';
+ break;
+ case 'A':
+ case 'a':
+ res *= base;
+ res += 10;
+ break;
+ case 'B':
+ case 'b':
+ res *= base;
+ res += 11;
+ break;
+ case 'C':
+ case 'c':
+ res *= base;
+ res += 12;
+ break;
+ case 'D':
+ case 'd':
+ res *= base;
+ res += 13;
+ break;
+ case 'E':
+ case 'e':
+ res *= base;
+ res += 14;
+ break;
+ case 'F':
+ case 'f':
+ res *= base;
+ res += 15;
+ break;
+ default:
+ return res;
+ }
+ buffer++;
+ }
+ return res;
+}
+
+void one_arg(char *buffer, int *a)
+{
+ while (*buffer && *buffer != '\n')
+ {
+ if (*buffer == ' ') buffer++;
+ else break;
+ }
+
+ *a = to_int(buffer);
+}
+
+void two_args(char *buffer, int *a, int *b)
+{
+ while (*buffer && *buffer != '\n')
+ {
+ if (*buffer == ' ') buffer++;
+ else break;
+ }
+
+ *a = to_int(buffer);
+
+ while (*buffer && *buffer != '\n')
+ {
+ if (*buffer != ' ') buffer++;
+ else break;
+ }
+
+ while (*buffer && *buffer != '\n')
+ {
+ if (*buffer == ' ') buffer++;
+ else break;
+ }
+
+ *b = to_int(buffer);
+}
+
+void do_inout(void)
+{
+ char buffer[256];
+ char *arg1, *arg2;
+ int a,b;
+
+ printf("In/Out Session\nUse 'i[bwl]' for in, 'o[bwl]' for out and 'q' to quit\n");
+
+ do
+ {
+ cons_gets(buffer);
+ printf("\n");
+
+ *arg1 = buffer;
+ while (*arg1 != ' ' ) arg1++;
+ while (*arg1 == ' ') arg1++;
+
+ if (buffer[0] == 'i')
+ {
+ one_arg(buffer+2, &a);
+ switch (buffer[1])
+ {
+ case 'b':
+ printf("in_byte(%xh) = %xh\n", a, A1_inb(a));
+ break;
+ case 'w':
+ printf("in_word(%xh) = %xh\n", a, A1_inw(a));
+ break;
+ case 'l':
+ printf("in_dword(%xh) = %xh\n", a, A1_inl(a));
+ break;
+ default:
+ printf("Invalid length '%c'\n", buffer[1]);
+ break;
+ }
+ }
+ else if (buffer[0] == 'o')
+ {
+ two_args(buffer+2, &a, &b);
+ switch (buffer[1])
+ {
+ case 'b':
+ printf("out_byte(%d, %d)\n", a, b);
+ A1_outb(a,b);
+ break;
+ case 'w':
+ printf("out_word(%d, %d)\n", a, b);
+ A1_outw(a, b);
+ break;
+ case 'l':
+ printf("out_long(%d, %d)\n", a, b);
+ A1_outl(a, b);
+ break;
+ default:
+ printf("Invalid length '%c'\n", buffer[1]);
+ break;
+ }
+ } else if (buffer[0] == 'q') return;
+ } while (1);
+}
OpenPOWER on IntegriCloud