#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; char *s; #ifdef EASTEREGG int easteregg_active = 0; #endif char *pal_reset; u8 *fb; unsigned char *msg; unsigned char current_attr; PRINTF("Trying to remove init data\n"); 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; } #if 1 /*def 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