diff options
author | Gwenolé Beauchesne <gbeauchesne@mandriva.org> | 2004-08-25 05:45:29 +0000 |
---|---|---|
committer | Gwenolé Beauchesne <gbeauchesne@mandriva.org> | 2004-08-25 05:45:29 +0000 |
commit | e0fbcfe868b509f256fcdc239178d32f617bb129 (patch) | |
tree | 26d7a7632d92979a6f7a39f0a707c935ca186e9f /tools/ddcprobe/int10/i10_v86.c | |
parent | 56e607a5ce0c98074b0c3067e2bba76e9d73223d (diff) | |
download | drakx-e0fbcfe868b509f256fcdc239178d32f617bb129.tar drakx-e0fbcfe868b509f256fcdc239178d32f617bb129.tar.gz drakx-e0fbcfe868b509f256fcdc239178d32f617bb129.tar.bz2 drakx-e0fbcfe868b509f256fcdc239178d32f617bb129.tar.xz drakx-e0fbcfe868b509f256fcdc239178d32f617bb129.zip |
VGA softbootloader for Linux, uses an x86 CPU emulator on non x86 arches.
Diffstat (limited to 'tools/ddcprobe/int10/i10_v86.c')
-rw-r--r-- | tools/ddcprobe/int10/i10_v86.c | 515 |
1 files changed, 515 insertions, 0 deletions
diff --git a/tools/ddcprobe/int10/i10_v86.c b/tools/ddcprobe/int10/i10_v86.c new file mode 100644 index 000000000..0e50b1576 --- /dev/null +++ b/tools/ddcprobe/int10/i10_v86.c @@ -0,0 +1,515 @@ +/* + * Copyright 1999 Egbert Eich + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the authors not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. The authors makes no representations + * about the suitability of this software for any purpose. It is provided + * "as is" without express or implied warranty. + * + * THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include <unistd.h> +#include <errno.h> +#include <asm/unistd.h> +#include <stdio.h> +#include <string.h> +#ifdef __i386__ +#include <sys/vm86.h> +#else +#include "vm86_struct.h" +#endif +#include <signal.h> +#include "v86bios.h" +#include "AsmMacros.h" + +extern int emu_vm86(struct vm86_struct *vm); + +#define INT2PTR(a) ((a) + (unsigned char *) 0) + +void log_err(char *format, ...) __attribute__ ((format (printf, 1, 2))); + +struct vm86_struct vm86s; + +static int vm86_GP_fault(void); +static int vm86_do_int(int num); +#ifdef __i386__ +static int vm86_rep(struct vm86_struct *ptr); +#endif +void log_registers(void); + +#define CPU_REG(x) (vm86s.regs.x) +#define CPU_REG_LW(reg) (*((CARD16 *)&CPU_REG(reg))) +#define CPU_REG_HW(reg) (*((CARD16 *)&CPU_REG(reg) + 1)) +#define CPU_REG_LB(reg) (*(CARD8 *)&CPU_REG(e##reg)) +#define SEG_ADR(type, seg, reg) type((CPU_REG_LW(seg) << 4) + CPU_REG_LW(e##reg) + (unsigned char *) 0) +#define DF (1 << 10) + +struct pio P; + +void +setup_io(void) +{ + P.inb = (CARD8(*)(CARD16))inb; + P.inw = (CARD16(*)(CARD16))inw; + P.inl = (CARD32(*)(CARD16))inl; + P.outb = (void(*)(CARD16,CARD8))outb; + P.outw = (void(*)(CARD16,CARD16))outw; + P.outl = (void(*)(CARD16,CARD32))outl; +} + + +static void +setup_vm86(unsigned long bios_start, i86biosRegsPtr regs) +{ + CARD32 eip; + CARD16 cs; + + vm86s.flags = VM86_SCREEN_BITMAP; + vm86s.flags = 0; + vm86s.screen_bitmap = 0; + vm86s.cpu_type = CPU_586; + memset(&vm86s.int_revectored, 0xff,sizeof(vm86s.int_revectored)) ; + memset(&vm86s.int21_revectored, 0xff,sizeof(vm86s.int21_revectored)) ; + + eip = bios_start & 0xFFFF; + cs = (bios_start & 0xFF0000) >> 4; + + CPU_REG(eax) = regs->ax; + CPU_REG(ebx) = regs->bx; + CPU_REG(ecx) = regs->cx; + CPU_REG(edx) = regs->dx; + CPU_REG(esi) = 0; + CPU_REG(edi) = regs->di; + CPU_REG(ebp) = 0; + CPU_REG(eip) = eip; + CPU_REG(cs) = cs; + CPU_REG(esp) = 0x100; + CPU_REG(ss) = 0x30; /* This is the standard pc bios stack */ + CPU_REG(es) = regs->es; + CPU_REG(ds) = 0x40; /* standard pc ds */ + CPU_REG(fs) = 0; + CPU_REG(gs) = 0; + CPU_REG(eflags) |= (VIF_MASK | VIP_MASK); +} + +void +collect_bios_regs(i86biosRegsPtr regs) +{ + regs->ax = CPU_REG(eax); + regs->bx = CPU_REG(ebx); + regs->cx = CPU_REG(ecx); + regs->dx = CPU_REG(edx); + regs->es = CPU_REG(es); + regs->ds = CPU_REG(ds); + regs->di = CPU_REG(edi); + regs->si = CPU_REG(esi); +} + +static int do_vm86(int cpuemu) +{ + int retval; + +#ifdef V86BIOS_DEBUG + dump_registers(); +#endif + +#ifdef __i386__ + if(cpuemu) { + retval = emu_vm86(&vm86s); + } + else { + retval = vm86_rep(&vm86s); + } +#else + retval = emu_vm86(&vm86s); +#endif + + switch (VM86_TYPE(retval)) { + case VM86_UNKNOWN: + if (!vm86_GP_fault()) + return 0; + break; + case VM86_STI: + log_err("vm86_sti :-((\n"); + log_registers(); + return 0; + case VM86_INTx: + if (!vm86_do_int(VM86_ARG(retval))) { + log_err("Unknown vm86_int: %X\n",VM86_ARG(retval)); + log_registers(); + return 0; + } + /* I'm not sure yet what to do if we can handle ints */ + break; + case VM86_SIGNAL: + log_err("VBE: received a signal!\n"); + log_registers(); + return 0; + default: + log_err("unknown type(0x%x)=0x%x\n", + VM86_ARG(retval),VM86_TYPE(retval)); + log_registers(); + return 0; + } + + return 1; +} + +void +do_x86(unsigned long bios_start, i86biosRegsPtr regs, int cpuemu) +{ + setup_vm86(bios_start, regs); + while(do_vm86(cpuemu)) {}; + collect_bios_regs(regs); +} + +/* get the linear address */ +#define LIN_PREF_SI ((pref_seg << 4) + CPU_REG_LW(esi)) + +#define LWECX (prefix66 ^ prefix67 ? CPU_REG(ecx) : CPU_REG_LW(ecx)) + +static int +vm86_GP_fault(void) +{ + unsigned char *csp, *lina; + CARD32 org_eip; + int pref_seg; + int done,is_rep,prefix66,prefix67; + + + csp = lina = SEG_ADR((unsigned char *), cs, ip); +#ifdef V86BIOS_DEBUG + printf("exception: \n"); + dump_code(); +#endif + + is_rep = 0; + prefix66 = prefix67 = 0; + pref_seg = -1; + + /* eat up prefixes */ + done = 0; + do { + switch (*(csp++)) { + case 0x66: /* operand prefix */ prefix66=1; break; + case 0x67: /* address prefix */ prefix67=1; break; + case 0x2e: /* CS */ pref_seg=CPU_REG(cs); break; + case 0x3e: /* DS */ pref_seg=CPU_REG(ds); break; + case 0x26: /* ES */ pref_seg=CPU_REG(es); break; + case 0x36: /* SS */ pref_seg=CPU_REG(ss); break; + case 0x65: /* GS */ pref_seg=CPU_REG(gs); break; + case 0x64: /* FS */ pref_seg=CPU_REG(fs); break; + case 0xf2: /* repnz */ + case 0xf3: /* rep */ is_rep=1; break; + default: done=1; + } + } while (!done); + csp--; /* oops one too many */ + org_eip = CPU_REG(eip); + CPU_REG_LW(eip) += (csp - lina); + + switch (*csp) { + + case 0x6c: /* insb */ + /* NOTE: ES can't be overwritten; prefixes 66,67 should use esi,edi,ecx + * but is anyone using extended regs in real mode? */ + /* WARNING: no test for DI wrapping! */ + CPU_REG_LW(edi) += port_rep_inb(CPU_REG_LW(edx), + SEG_ADR((CARD8 *),es,di), + CPU_REG_LW(eflags)&DF, + (is_rep? LWECX:1)); + if (is_rep) LWECX = 0; + CPU_REG_LW(eip)++; + break; + + case 0x6d: /* (rep) insw / insd */ + /* NOTE: ES can't be overwritten */ + /* WARNING: no test for _DI wrapping! */ + if (prefix66) { + CPU_REG_LW(edi) += port_rep_inl(CPU_REG_LW(edx), + SEG_ADR((CARD32 *),es,di), + CPU_REG_LW(eflags)&DF, + (is_rep? LWECX:1)); + } + else { + CPU_REG_LW(edi) += port_rep_inw(CPU_REG_LW(edx), + SEG_ADR((CARD16 *),es,di), + CPU_REG_LW(eflags)&DF, + (is_rep? LWECX:1)); + } + if (is_rep) LWECX = 0; + CPU_REG_LW(eip)++; + break; + + case 0x6e: /* (rep) outsb */ + if (pref_seg < 0) pref_seg = CPU_REG_LW(ds); + /* WARNING: no test for _SI wrapping! */ + CPU_REG_LW(esi) += port_rep_outb(CPU_REG_LW(edx),(CARD8*)INT2PTR(LIN_PREF_SI), + CPU_REG_LW(eflags)&DF, + (is_rep? LWECX:1)); + if (is_rep) LWECX = 0; + CPU_REG_LW(eip)++; + break; + + case 0x6f: /* (rep) outsw / outsd */ + if (pref_seg < 0) pref_seg = CPU_REG_LW(ds); + /* WARNING: no test for _SI wrapping! */ + if (prefix66) { + CPU_REG_LW(esi) += port_rep_outl(CPU_REG_LW(edx), + (CARD32 *)INT2PTR(LIN_PREF_SI), + CPU_REG_LW(eflags)&DF, + (is_rep? LWECX:1)); + } + else { + CPU_REG_LW(esi) += port_rep_outw(CPU_REG_LW(edx), + (CARD16 *)INT2PTR(LIN_PREF_SI), + CPU_REG_LW(eflags)&DF, + (is_rep? LWECX:1)); + } + if (is_rep) LWECX = 0; + CPU_REG_LW(eip)++; + break; + + case 0xe5: /* inw xx, inl xx */ + if (prefix66) CPU_REG(eax) = P.inl((int) csp[1]); + else CPU_REG_LW(eax) = P.inw((int) csp[1]); + CPU_REG_LW(eip) += 2; + break; + case 0xe4: /* inb xx */ + CPU_REG_LW(eax) &= ~(CARD32)0xff; + CPU_REG_LB(ax) |= P.inb((int) csp[1]); + CPU_REG_LW(eip) += 2; + break; + case 0xed: /* inw dx, inl dx */ + if (prefix66) CPU_REG(eax) = P.inl(CPU_REG_LW(edx)); + else CPU_REG_LW(eax) = P.inw(CPU_REG_LW(edx)); + CPU_REG_LW(eip) += 1; + break; + case 0xec: /* inb dx */ + CPU_REG_LW(eax) &= ~(CARD32)0xff; + CPU_REG_LB(ax) |= P.inb(CPU_REG_LW(edx)); + CPU_REG_LW(eip) += 1; + break; + + case 0xe7: /* outw xx */ + if (prefix66) P.outl((int)csp[1], CPU_REG(eax)); + else P.outw((int)csp[1], CPU_REG_LW(eax)); + CPU_REG_LW(eip) += 2; + break; + case 0xe6: /* outb xx */ + P.outb((int) csp[1], CPU_REG_LB(ax)); + CPU_REG_LW(eip) += 2; + break; + case 0xef: /* outw dx */ + if (prefix66) P.outl(CPU_REG_LW(edx), CPU_REG(eax)); + else P.outw(CPU_REG_LW(edx), CPU_REG_LW(eax)); + CPU_REG_LW(eip) += 1; + break; + case 0xee: /* outb dx */ + P.outb(CPU_REG_LW(edx), CPU_REG_LB(ax)); + CPU_REG_LW(eip) += 1; + break; + + case 0xf4: +#ifdef V86BIOS_DEBUG + printf("hlt at %p\n", lina); +#endif + return 0; + + case 0x0f: + log_err("CPU 0x0f Trap at eip=0x%lx\n",CPU_REG(eip)); + goto op0ferr; + break; + + case 0xf0: /* lock */ + default: + log_err("unknown reason for exception\n"); + log_registers(); + op0ferr: + log_err("cannot continue\n"); + return 0; + } /* end of switch() */ + return 1; +} + +static int +vm86_do_int(int num) +{ + int val; + struct regs86 regs; + + /* try to run bios interrupt */ + + /* if not installed fall back */ +#define COPY(x) regs.x = CPU_REG(x) +#define COPY_R(x) CPU_REG(x) = regs.x + + COPY(eax); + COPY(ebx); + COPY(ecx); + COPY(edx); + COPY(esi); + COPY(edi); + COPY(ebp); + COPY(eip); + COPY(esp); + COPY(cs); + COPY(ss); + COPY(ds); + COPY(es); + COPY(fs); + COPY(gs); + COPY(eflags); + + if (!(val = int_handler(num,®s))) + if (!(val = run_bios_int(num,®s))) + return val; + + COPY_R(eax); + COPY_R(ebx); + COPY_R(ecx); + COPY_R(edx); + COPY_R(esi); + COPY_R(edi); + COPY_R(ebp); + COPY_R(eip); + COPY_R(esp); + COPY_R(cs); + COPY_R(ss); + COPY_R(ds); + COPY_R(es); + COPY_R(fs); + COPY_R(gs); + COPY_R(eflags); + + return val; +#undef COPY +#undef COPY_R +} + +#ifdef __i386__ + +static int +vm86_rep(struct vm86_struct *ptr) +{ + + int __res; + + /* stay away from %ebx */ + __asm__ __volatile__("push %%ebx\n\tmov %%ecx,%%ebx\n\tpush %%gs\n\tint $0x80\n\tpop %%gs\n\tpop %%ebx\n" + :"=a" (__res):"a" ((int)113), + "c" ((struct vm86_struct *)ptr)); + + if ((__res) < 0) { + errno = -__res; + __res=-1; + } + else errno = 0; + return __res; +} + +#endif + +#ifdef __i386__ + +#define pushw(base, ptr, val) \ +__asm__ __volatile__( \ + "decw %w0\n\t" \ + "movb %h2,(%1,%0)\n\t" \ + "decw %w0\n\t" \ + "movb %b2,(%1,%0)" \ + : "=r" (ptr) \ + : "r" (base), "q" (val), "0" (ptr)) + +#else + +#define pushw(base, ptr, val) { \ + ptr = ((ptr) - 1) & 0xffff; \ + *((unsigned char *)(base) + (ptr)) = (val) >> 8; \ + ptr = ((ptr) - 1) & 0xffff; \ + *((unsigned char *)(base) + (ptr)) = (val); \ + } + +#endif + +int +run_bios_int(int num, struct regs86 *regs) +{ + CARD16 *ssp; + CARD32 sp; + CARD32 eflags; + +#ifdef V86BIOS_DEBUG + static int firsttime = 1; +#endif + /* check if bios vector is initialized */ + if (((CARD16*)0)[(num<<1)+1] == 0x0000) { /* SYS_BIOS_SEG ?*/ + return 0; + } + +#ifdef V86BIOS_DEBUG + if (firsttime) { + dprint(0,0x3D0); + firsttime = 0; + } +#endif + + ssp = (CARD16*)INT2PTR(CPU_REG(ss)<<4); + sp = (CARD32) CPU_REG_LW(esp); + + eflags = regs->eflags; + eflags = ((eflags & VIF_MASK) != 0) + ? (eflags | IF_MASK) : (eflags & ~(CARD32) IF_MASK); + pushw(ssp, sp, eflags); + pushw(ssp, sp, regs->cs); + pushw(ssp, sp, (CARD16)regs->eip); + regs->esp -= 6; + regs->cs = ((CARD16 *) 0)[(num << 1) + 1]; + regs->eip = (regs->eip & 0xFFFF0000) | ((CARD16 *) 0)[num << 1]; +#ifdef V86BIOS_DEBUG + dump_code(); +#endif + regs->eflags = regs->eflags + & ~(VIF_MASK | TF_MASK | IF_MASK | NT_MASK); + return 1; +} + +CARD32 +getIntVect(int num) +{ + return ((CARD32*)0)[num]; +} + +CARD32 +getIP(void) +{ + return (CPU_REG(cs) << 4) + CPU_REG(eip); +} + +void log_registers() +{ + log_err( + " eax %08x, ebx %08x, ecx %08x, edx %08x\n" + " esi %08x, edi %08x, ebp %08x, esp %08x\n" + " ds %04x, es %04x, fs %04x, gs %04x, ss %04x\n" + " cs:eip %04x:%08x\n", + (unsigned) CPU_REG(eax), (unsigned) CPU_REG(ebx), (unsigned) CPU_REG(ecx), (unsigned) CPU_REG(edx), + (unsigned) CPU_REG(esi), (unsigned) CPU_REG(edi), (unsigned) CPU_REG(ebp), (unsigned) CPU_REG(esp), + (unsigned) CPU_REG(ds), (unsigned) CPU_REG(es), + (unsigned) CPU_REG(fs), (unsigned) CPU_REG(gs), (unsigned) CPU_REG(ss), + (unsigned) CPU_REG(cs), (unsigned) CPU_REG(eip) + ); +} + |