summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Reinauer <stefan.reinauer@coreboot.org>2009-05-29 13:09:57 +0000
committerStefan Reinauer <stefan.reinauer@coreboot.org>2009-05-29 13:09:57 +0000
commit881db0ca0c8dda16d289b21b1d107b330ea556f4 (patch)
tree20dc13dd2d3b6e8d8ab3df0e517ba868ffad3e2a
parent50b961ea7ad611e8fc05e057b0a2bd36d2ef4ec6 (diff)
downloadcoreboot-881db0ca0c8dda16d289b21b1d107b330ea556f4.tar.gz
coreboot-881db0ca0c8dda16d289b21b1d107b330ea556f4.tar.bz2
coreboot-881db0ca0c8dda16d289b21b1d107b330ea556f4.zip
Part II - svn externals need to be committed separately
drop most of the crappy vm86 code and replace it with a rewritten version that has all assembler in a .S file and all C code in a .c file. Also, remove requirement to move around between GDTs. This version includes the suggestions from Peter to clean up CR0 manipulation and to guard critical code paths by cli/sti. Tested and working on my hardware. Signed-off-by: Stefan Reinauer <stepan@coresystems.de> Acked-by: Peter Stuge <peter@stuge.se> git-svn-id: svn://coreboot.org/repository/coreboot-v3@1167 f3766cd6-281f-0410-b1cd-43a5c92072e9
-rw-r--r--util/x86emu/Config.lb5
-rw-r--r--util/x86emu/Makefile2
-rw-r--r--util/x86emu/biosemu.c3
-rw-r--r--util/x86emu/vm86.c719
-rw-r--r--util/x86emu/vm86_gdt.c101
-rw-r--r--util/x86emu/x86.c228
-rw-r--r--util/x86emu/x86_asm.S392
-rw-r--r--util/x86emu/x86_interrupts.c243
8 files changed, 869 insertions, 824 deletions
diff --git a/util/x86emu/Config.lb b/util/x86emu/Config.lb
index 3f925f76cd83..0721ecdfcd23 100644
--- a/util/x86emu/Config.lb
+++ b/util/x86emu/Config.lb
@@ -6,8 +6,9 @@ if CONFIG_PCI_OPTION_ROM_RUN_YABEL
dir x86emu
else
if CONFIG_PCI_OPTION_ROM_RUN_VM86
- object vm86.o
- object vm86_gdt.o
+ object x86.o
+ object x86_interrupts.o
+ object x86_asm.S
else
object biosemu.o
dir pcbios
diff --git a/util/x86emu/Makefile b/util/x86emu/Makefile
index 02f9f986b9d2..3cc985a8c6fd 100644
--- a/util/x86emu/Makefile
+++ b/util/x86emu/Makefile
@@ -37,7 +37,7 @@ X86EMU_INCLUDE += -I $(src)/util/x86emu
#TODO: remove these, these are .h files from slof, to make the merge easier...
X86EMU_INCLUDE += -I $(src)/util/x86emu/yabel/compat
endif
-VM86_SRC = vm86.c vm86_gdt.c
+VM86_SRC = x86.c x86_asm.S
ifeq ($(CONFIG_PCI_OPTION_ROM_RUN_X86EMU),y)
LIBX86EMU_SRC=$(patsubst %,x86emu/%,$(X86EMU_SRC)) $(BIOSEMU_SRC)
diff --git a/util/x86emu/biosemu.c b/util/x86emu/biosemu.c
index b924092e9918..9b6dcb472ec1 100644
--- a/util/x86emu/biosemu.c
+++ b/util/x86emu/biosemu.c
@@ -213,6 +213,7 @@ void do_int(int num)
ret = run_bios_int(num);
}
+
#if 0
#define SYS_BIOS 0xf0000
/*
@@ -386,7 +387,7 @@ void run_bios(struct device * dev, unsigned long addr)
pushw(X86_SS);
pushw(X86_SP + 2);
-#ifndef NO_TRACE
+#ifdef DEBUG
//X86EMU_trace_on();
#endif
diff --git a/util/x86emu/vm86.c b/util/x86emu/vm86.c
index d38ec00984ba..e69de29bb2d1 100644
--- a/util/x86emu/vm86.c
+++ b/util/x86emu/vm86.c
@@ -1,719 +0,0 @@
-/*
- * Erik Arjan Hendriks <hendriks@lanl.gov>
- * Copyright (C) 2000 Scyld.
- * Copyright (C) 2000 Scyld Computing Corporation
- * Copyright (C) 2001 University of California. LA-CC Number 01-67.
- * Copyright (C) 2005 Nick.Barker9@btinternet.com
- * Copyright (C) 2007-2009 coresystems GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-
-#include <device/pci.h>
-#include <device/pci_ids.h>
-#include <device/pci_ops.h>
-#include <string.h>
-#ifdef COREBOOT_V2
-#include <console/console.h>
-#include <arch/io.h>
-#define printk(lvl, x...) printk_debug(x)
-#else
-#include <console.h>
-#include <io.h>
-#endif
-
-/* The address arguments to this function are PHYSICAL ADDRESSES */
-static void real_mode_switch_call_vga(unsigned long devfn)
-{
- __asm__ __volatile__ (
- /* paranoia -- does ecx get saved? not sure.
- * This is the easiest safe thing to do.
- */
- " pushal \n"
- /* save the stack */
- " mov %esp, __stack \n"
- " jmp 1f \n"
- "__stack: .long 0 \n"
- "1:\n"
- /* get devfn into %ecx */
- " movl %esp, %ebp \n"
- // FIXME: why is this 8?
- " movl 8(%ebp), %ecx \n"
- /* load 'our' gdt */
- " lgdt %cs:__mygdtaddr \n"
-
- /* This configures CS properly for real mode. */
- " ljmp $0x28, $__rms_16bit\n"
- "__rms_16bit: \n"
- " .code16 \n"
- /* 16 bit code from here on... */
-
- /* Load the segment registers w/ properly configured
- * segment descriptors. They will retain these
- * configurations (limits, writability, etc.) once
- * protected mode is turned off.
- */
- " mov $0x30, %ax \n"
- " mov %ax, %ds \n"
- " mov %ax, %es \n"
- " mov %ax, %fs \n"
- " mov %ax, %gs \n"
- " mov %ax, %ss \n"
-
- /* Turn off protection (bit 0 in CR0) */
- " movl %cr0, %eax \n"
- " andl $0xFFFFFFFE, %eax \n"
- " movl %eax, %cr0 \n"
-
- /* Now really going into real mode */
- " ljmp $0, $__rms_real\n"
- "__rms_real: \n"
-
- /* Setup a stack: Put the stack at the end of page zero.
- * That way we can easily share it between real and
- * protected, since the 16-bit ESP at segment 0 will
- * work for any case. */
- " mov $0x0, %ax \n"
- " mov %ax, %ss \n"
- " movl $0x1000, %eax \n"
- " movl %eax, %esp \n"
-
- /* Load our 16 it idt */
- " xor %ax, %ax \n"
- " mov %ax, %ds \n"
- " lidt __myidt \n"
-
- /* Dump zeros in the other segment registers */
- " mov %ax, %es \n"
- " mov %ax, %fs \n"
- " mov %ax, %gs \n"
- " mov $0x40, %ax \n"
- " mov %ax, %ds \n"
- " mov %cx, %ax \n"
-
- /* run VGA BIOS at 0xc000:0003 */
- " lcall $0xc000, $0x0003\n"
-
- /* If we got here, just about done.
- * Need to get back to protected mode
- */
- " movl %cr0, %eax \n"
- " orl $0x0000001, %eax\n" /* PE = 1 */
- " movl %eax, %cr0 \n"
-
- /* Now that we are in protected mode
- * jump to a 32 bit code segment.
- */
- " data32 ljmp $0x10, $vgarestart\n"
- "vgarestart:\n"
- " .code32\n"
- " movw $0x18, %ax \n"
- " mov %ax, %ds \n"
- " mov %ax, %es \n"
- " mov %ax, %fs \n"
- " mov %ax, %gs \n"
- " mov %ax, %ss \n"
-
- /* restore proper gdt and idt */
- " lgdt %cs:gdtarg \n"
- " lidt idtarg \n"
-
- ".globl vga_exit \n"
- "vga_exit: \n"
- " mov __stack, %esp \n"
- " popal \n"
- );
-}
-
-// FIXME: drop this
-// __asm__ (".text\n""real_mode_switch_end:\n");
-// extern char real_mode_switch_end[];
-
-/* call vga bios int 10 function 0x4f14 to enable main console
- epia-m does not always autosence the main console so forcing it on is good !! */
-void vga_enable_console(void)
-{
- __asm__ __volatile__ (
- /* paranoia -- does ecx get saved? not sure. This is
- * the easiest safe thing to do. */
- " pushal \n"
- /* save the stack */
- " mov %esp, __stack \n"
-
- /* load 'our' gdt */
- " lgdt %cs:__mygdtaddr \n"
-
- /* This configures CS properly for real mode. */
- " ljmp $0x28, $__vga_ec_16bit\n"
- "__vga_ec_16bit: \n"
- " .code16 \n"
- /* 16 bit code from here on... */
-
- /* Load the segment registers w/ properly configured segment
- * descriptors. They will retain these configurations (limits,
- * writability, etc.) once protected mode is turned off. */
- " mov $0x30, %ax \n"
- " mov %ax, %ds \n"
- " mov %ax, %es \n"
- " mov %ax, %fs \n"
- " mov %ax, %gs \n"
- " mov %ax, %ss \n"
-
- /* Turn off protection (bit 0 in CR0) */
- " movl %cr0, %eax \n"
- " andl $0xFFFFFFFE, %eax\n"
- " movl %eax, %cr0 \n"
-
- /* Now really going into real mode */
- " ljmp $0, $__vga_ec_real \n"
- "__vga_ec_real: \n"
-
- /* put the stack at the end of page zero.
- * that way we can easily share it between real and protected,
- * since the 16-bit ESP at segment 0 will work for any case. */
- /* Setup a stack */
- " mov $0x0, %ax \n"
- " mov %ax, %ss \n"
- " movl $0x1000, %eax \n"
- " movl %eax, %esp \n"
-
- /* debugging for RGM */
- " mov $0x11, %al \n"
- " outb %al, $0x80 \n"
-
- /* Load our 16 it idt */
- " xor %ax, %ax \n"
- " mov %ax, %ds \n"
- " lidt __myidt \n"
-
- /* Dump zeros in the other segregs */
- " mov %ax, %ds \n"
- " mov %ax, %es \n"
- " mov %ax, %fs \n"
- " mov %ax, %gs \n"
-
- /* ask bios to enable main console */
- /* set up for int 10 call - values found from X server
- * bios call routines */
- " movw $0x4f14,%ax \n"
- " movw $0x8003,%bx \n"
- " movw $1, %cx \n"
- " movw $0, %dx \n"
- " movw $0, %di \n"
- " int $0x10 \n"
-
- " movb $0x55, %al \n"
- " outb %al, $0x80 \n"
-
- /* if we got here, just about done.
- * Need to get back to protected mode */
- " movl %cr0, %eax \n"
- " orl $0x0000001, %eax\n" /* PE = 1 */
- " movl %eax, %cr0 \n"
-
- /* Now that we are in protected mode jump to a 32 bit code segment. */
- " data32 ljmp $0x10, $vga_ec_restart\n"
- "vga_ec_restart:\n"
- " .code32\n"
- " movw $0x18, %ax \n"
- " mov %ax, %ds \n"
- " mov %ax, %es \n"
- " mov %ax, %fs \n"
- " mov %ax, %gs \n"
- " mov %ax, %ss \n"
-
- /* restore proper gdt and idt */
- " lgdt %cs:gdtarg \n"
- " lidt idtarg \n"
- " .globl vga__ec_exit \n"
- "vga_ec_exit:\n"
- " mov __stack, %esp \n"
- " popal\n"
- );
-}
-
-// we had hoped to avoid this.
-// this is a stub IDT only. It's main purpose is to ignore calls
-// to the BIOS.
-// no longer. Dammit. We have to respond to these.
-struct realidt {
- unsigned short offset, cs;
-};
-
-// from a handy writeup that andrey found.
-
-// handler.
-// There are some assumptions we can make here.
-// First, the Top Of Stack (TOS) is located on the top of page zero.
-// we can share this stack between real and protected mode.
-// that simplifies a lot of things ...
-// we'll just push all the registers on the stack as longwords,
-// and pop to protected mode.
-// second, since this only ever runs as part of coreboot,
-// we know all the segment register values -- so we don't save any.
-// keep the handler that calls things small. It can do a call to
-// more complex code in coreboot itself. This helps a lot as we don't
-// have to do address fixup in this little stub, and calls are absolute
-// so the handler is relocatable.
-void handler(void)
-{
- __asm__ __volatile__ (
- " .code16 \n"
- "idthandle: \n"
- " pushal \n"
- " movb $0, %al \n"
- " ljmp $0, $callbiosint16\n"
- "end_idthandle: \n"
- " .code32 \n"
- );
-}
-
-void debughandler(void)
-{
- __asm__ __volatile__ (
- " .code16 \n"
- "debughandle: \n"
- " pushw %cx \n"
- " movw $250, %cx \n"
- "dbh1: \n"
- " loop dbh1 \n"
- " popw %cx \n"
- " iret \n"
- "end_debughandle: \n"
- ".code32 \n"
- );
-}
-
-// Calling conventions. The first C function is called with this stuff
-// on the stack. They look like value parameters, but note that if you
-// modify them they will go back to the INTx function modified.
-// the C function will call the biosint function with these as
-// REFERENCE parameters. In this way, we can easily get
-// returns back to the INTx caller (i.e. vgabios)
-void callbiosint(void)
-{
- __asm__ __volatile__ (
- " .code16 \n"
- "callbiosint16: \n"
- " push %ds \n"
- " push %es \n"
- " push %fs \n"
- " push %gs \n"
- // clean up the int #. To save space we put it in the lower
- // byte. But the top 24 bits are junk.
- " andl $0xff, %eax\n"
- // this push does two things:
- // - put the INT # on the stack as a parameter
- // - provides us with a temp for the %cr0 mods.
- " pushl %eax \n"
- " movl %cr0, %eax\n"
- " orl $0x00000001, %eax\n" /* PE = 1 */
- " movl %eax, %cr0\n"
- /* Now that we are in protected mode jump to a 32 bit code segment. */
- " data32 ljmp $0x10, $biosprotect\n"
- "biosprotect: \n"
- " .code32 \n"
- " movw $0x18, %ax \n"
- " mov %ax, %ds \n"
- " mov %ax, %es \n"
- " mov %ax, %fs \n"
- " mov %ax, %gs \n"
- " mov %ax, %ss \n"
- " lidt idtarg \n"
- " call biosint \n"
- // back to real mode ...
- " ljmp $0x28, $__rms_16bit2\n"
- "__rms_16bit2: \n"
- " .code16 \n"
- /* 16 bit code from here on... */
- /* Load the segment registers w/ properly configured segment
- * descriptors. They will retain these configurations (limits,
- * writability, etc.) once protected mode is turned off. */
- " mov $0x30, %ax \n"
- " mov %ax, %ds \n"
- " mov %ax, %es \n"
- " mov %ax, %fs \n"
- " mov %ax, %gs \n"
- " mov %ax, %ss \n"
-
- /* Turn off protection (bit 0 in CR0) */
- " movl %cr0, %eax \n"
- " andl $0xFFFFFFFE, %eax \n"
- " movl %eax, %cr0 \n"
-
- /* Now really going into real mode */
- " ljmp $0, $__rms_real2 \n"
- "__rms_real2: \n"
-
- /* Setup a stack
- * FixME: where is esp? */
- " mov $0x0, %ax \n"
- " mov %ax, %ss \n"
-
- /* ebugging for RGM */
- " mov $0x11, %al \n"
- " outb %al, $0x80 \n"
-
- /* Load our 16 it idt */
- " xor %ax, %ax \n"
- " mov %ax, %ds \n"
- " lidt __myidt \n"
-
- /* Dump zeros in the other segregs */
- " mov %ax, %es \n"
- " mov %ax, %fs \n"
- " mov %ax, %gs \n"
- " mov $0x40, %ax \n"
- " mov %ax, %ds \n"
-
- /* pop the INT # that you pushed earlier */
- " popl %eax \n"
- " pop %gs \n"
- " pop %fs \n"
- " pop %es \n"
- " pop %ds \n"
- " popal \n"
- " iret \n"
- " .code32 \n"
- );
-}
-
-enum {
- PCIBIOS = 0x1a,
- MEMSIZE = 0x12
-};
-
-int pcibios(unsigned long *pedi, unsigned long *pesi, unsigned long *pebp,
- unsigned long *pesp, unsigned long *pebx, unsigned long *pedx,
- unsigned long *pecx, unsigned long *peax, unsigned long *pflags);
-
-int handleint21(unsigned long *pedi, unsigned long *pesi, unsigned long *pebp,
- unsigned long *pesp, unsigned long *pebx, unsigned long *pedx,
- unsigned long *pecx, unsigned long *peax, unsigned long *pflags
- );
-
-extern void vga_exit(void);
-
-int __attribute__((regparm(0))) biosint(unsigned long intnumber,
- unsigned long gsfs, unsigned long dses,
- unsigned long edi, unsigned long esi,
- unsigned long ebp, unsigned long esp,
- unsigned long ebx, unsigned long edx,
- unsigned long ecx, unsigned long eax,
- unsigned long cs_ip, unsigned short stackflags)
-{
- unsigned long ip;
- unsigned long cs;
- unsigned long flags;
- int ret = -1;
-
- ip = cs_ip & 0xffff;
- cs = cs_ip >> 16;
- flags = stackflags;
-
- printk(BIOS_DEBUG, "biosint: INT# 0x%lx\n", intnumber);
- printk(BIOS_DEBUG, "biosint: eax 0x%lx ebx 0x%lx ecx 0x%lx edx 0x%lx\n",
- eax, ebx, ecx, edx);
- printk(BIOS_DEBUG, "biosint: ebp 0x%lx esp 0x%lx edi 0x%lx esi 0x%lx\n",
- ebp, esp, edi, esi);
- printk(BIOS_DEBUG, "biosint: ip 0x%lx cs 0x%lx flags 0x%lx\n",
- ip, cs, flags);
-
- // cases in a good compiler are just as good as your own tables.
- switch (intnumber) {
- case 0 ... 15:
- // These are not BIOS service, but the CPU-generated exceptions
- printk(BIOS_INFO, "biosint: Oops, exception %lu\n", intnumber);
- if (esp < 0x1000) {
- printk(BIOS_DEBUG, "Stack contents: ");
- while (esp < 0x1000) {
- printk(BIOS_DEBUG, "0x%04x ", *(unsigned short *) esp);
- esp += 2;
- }
- printk(BIOS_DEBUG, "\n");
- }
- printk(BIOS_DEBUG, "biosint: Bailing out\n");
- // "longjmp"
- vga_exit();
- break;
-
- case PCIBIOS:
- ret = pcibios( &edi, &esi, &ebp, &esp,
- &ebx, &edx, &ecx, &eax, &flags);
- break;
- case MEMSIZE:
- // who cares.
- eax = 64 * 1024;
- ret = 0;
- break;
- case 0x15:
- ret=handleint21( &edi, &esi, &ebp, &esp,
- &ebx, &edx, &ecx, &eax, &flags);
- break;
- default:
- printk(BIOS_INFO, "BIOSINT: Unsupport int #0x%lx\n",
- intnumber);
- break;
- }
- if (ret)
- flags |= 1; // carry flags
- else
- flags &= ~1;
- stackflags = flags;
- return ret;
-}
-
-void setup_realmode_idt(void)
-{
- extern unsigned char idthandle, end_idthandle;
-#if 0
- extern unsigned char debughandle, end_debughandle;
-#endif
-
- int i;
- struct realidt *idts = (struct realidt *) 0;
- int codesize = &end_idthandle - &idthandle;
- unsigned char *intbyte, *codeptr;
-
- // for each int, we create a customized little handler
- // that just pushes %ax, puts the int # in %al,
- // then calls the common interrupt handler.
- // this necessitated because intel didn't know much about
- // architecture when they did the 8086 (it shows)
- // (hmm do they know anymore even now :-)
- // obviously you can see I don't really care about memory
- // efficiency. If I did I would probe back through the stack
- // and get it that way. But that's really disgusting.
- for (i = 0; i < 256; i++) {
- idts[i].cs = 0;
- codeptr = (unsigned char *) 4096 + i * codesize;
- idts[i].offset = (unsigned) codeptr;
- memcpy(codeptr, &idthandle, codesize);
- intbyte = codeptr + 3;
- *intbyte = i;
- }
-
- // fixed entry points
-
- // VGA BIOSes tend to hardcode f000:f065 as the previous handler of
- // int10.
- // calling convention here is the same as INTs, we can reuse
- // the int entry code.
- codeptr = (unsigned char *) 0xff065;
- memcpy(codeptr, &idthandle, codesize);
- intbyte = codeptr + 3;
- *intbyte = 0x42; /* int42 is the relocated int10 */
-
- /* The source of the following code is not yet known.
- * We feel it may be useful someday, but right now it
- * scribbles over code space. We are leaving it here as a
- * "Living comment" since it may at some point be needed
- * again. It is a very intriguing idea -- one could run
- * vm86 code with TF set and set programmable times
- * between instructions to slow them down. For those who
- * recall the "turbo" switch on old PCs, this is the
- * software equivalent.
- */
-#if 0
- /* debug handler - useful to set a programmable delay between instructions if the
- TF bit is set upon call to real mode */
- idts[1].cs = 0;
- idts[1].offset = 16384;
- memcpy((void *)16384, &debughandle, &end_debughandle - &debughandle);
-#endif
-}
-
-void run_bios(struct device *dev, unsigned long addr)
-{
- int i;
-
- /* clear vga bios data area */
- for (i = 0x400; i < 0x500; i++) {
- *(unsigned char *) i = 0;
- }
- setup_realmode_idt();
- real_mode_switch_call_vga((dev->bus->secondary << 8) | dev->path.pci.devfn);
-}
-
-enum {
- CHECK = 0xb001,
- FINDDEV = 0xb102,
- READCONFBYTE = 0xb108,
- READCONFWORD = 0xb109,
- READCONFDWORD = 0xb10a,
- WRITECONFBYTE = 0xb10b,
- WRITECONFWORD = 0xb10c,
- WRITECONFDWORD = 0xb10d
-};
-
-// errors go in AH. Just set these up so that word assigns
-// will work. KISS.
-enum {
- PCIBIOS_NODEV = 0x8600,
- PCIBIOS_BADREG = 0x8700
-};
-
-int
-pcibios(unsigned long *pedi, unsigned long *pesi, unsigned long *pebp,
- unsigned long *pesp, unsigned long *pebx, unsigned long *pedx,
- unsigned long *pecx, unsigned long *peax, unsigned long *pflags)
-{
- unsigned short func = (unsigned short) *peax;
- int retval = 0;
- unsigned short devid, vendorid, devfn;
- /* Use short to get rid of gabage in upper half of 32-bit register */
- short devindex;
- unsigned char bus;
- struct device *dev;
-
- switch(func) {
- case CHECK:
- *pedx = 0x4350;
- *pecx = 0x2049;
- retval = 0;
- break;
- case FINDDEV:
- {
- devid = *pecx;
- vendorid = *pedx;
- devindex = *pesi;
- dev = 0;
-#ifdef COREBOOT_V2
- while ((dev = dev_find_device(vendorid, devid, dev))) {
-#else
- while ((dev = dev_find_pci_device(vendorid, devid, dev))) {
-#endif
- if (devindex <= 0)
- break;
- devindex--;
- }
- if (dev) {
- unsigned short busdevfn;
- *peax = 0;
- // busnum is an unsigned char;
- // devfn is an int, so we mask it off.
- busdevfn = (dev->bus->secondary << 8)
- | (dev->path.pci.devfn & 0xff);
- printk(BIOS_DEBUG, "0x%x: return 0x%x\n", func, busdevfn);
- *pebx = busdevfn;
- retval = 0;
- } else {
- *peax = PCIBIOS_NODEV;
- retval = -1;
- }
- }
- break;
- case READCONFDWORD:
- case READCONFWORD:
- case READCONFBYTE:
- case WRITECONFDWORD:
- case WRITECONFWORD:
- case WRITECONFBYTE:
- {
- unsigned long dword;
- unsigned short word;
- unsigned char byte;
- unsigned char reg;
-
- devfn = *pebx & 0xff;
- bus = *pebx >> 8;
- reg = *pedi;
- dev = dev_find_slot(bus, devfn);
- if (! dev) {
- printk(BIOS_DEBUG, "0x%x: BAD DEVICE bus %d devfn 0x%x\n", func, bus, devfn);
- // idiots. the pcibios guys assumed you'd never pass a bad bus/devfn!
- *peax = PCIBIOS_BADREG;
- retval = -1;
- }
- switch(func) {
- case READCONFBYTE:
- byte = pci_read_config8(dev, reg);
- *pecx = byte;
- break;
- case READCONFWORD:
- word = pci_read_config16(dev, reg);
- *pecx = word;
- break;
- case READCONFDWORD:
- dword = pci_read_config32(dev, reg);
- *pecx = dword;
- break;
- case WRITECONFBYTE:
- byte = *pecx;
- pci_write_config8(dev, reg, byte);
- break;
- case WRITECONFWORD:
- word = *pecx;
- pci_write_config16(dev, reg, word);
- break;
- case WRITECONFDWORD:
- dword = *pecx;
- pci_write_config32(dev, reg, dword);
- break;
- }
-
- if (retval)
- retval = PCIBIOS_BADREG;
- printk(BIOS_DEBUG, "0x%x: bus %d devfn 0x%x reg 0x%x val 0x%lx\n",
- func, bus, devfn, reg, *pecx);
- *peax = 0;
- retval = 0;
- }
- break;
- default:
- printk(BIOS_ERR, "UNSUPPORTED PCIBIOS FUNCTION 0x%x\n", func);
- break;
- }
-
- return retval;
-}
-
-int handleint21(unsigned long *edi, unsigned long *esi, unsigned long *ebp,
- unsigned long *esp, unsigned long *ebx, unsigned long *edx,
- unsigned long *ecx, unsigned long *eax, unsigned long *flags)
-{
- int res=-1;
- switch(*eax&0xffff)
- {
- case 0x5f19:
- break;
- case 0x5f18:
- *eax=0x5f;
- *ebx=0x545; // MCLK = 133, 32M frame buffer, 256 M main memory
- *ecx=0x060;
- res=0;
- break;
- case 0x5f00:
- *eax = 0x8600;
- break;
- case 0x5f01:
- *eax = 0x5f;
- *ecx = (*ecx & 0xffffff00 ) | 2; // panel type = 2 = 1024 * 768
- res = 0;
- break;
- case 0x5f02:
- *eax=0x5f;
- *ebx= (*ebx & 0xffff0000) | 2;
- *ecx= (*ecx & 0xffff0000) | 0x401; // PAL + crt only
- *edx= (*edx & 0xffff0000) | 0; // TV Layout - default
- res=0;
- break;
- case 0x5f0f:
- *eax=0x860f;
- break;
- }
- return res;
-}
diff --git a/util/x86emu/vm86_gdt.c b/util/x86emu/vm86_gdt.c
index 465d913105cd..e69de29bb2d1 100644
--- a/util/x86emu/vm86_gdt.c
+++ b/util/x86emu/vm86_gdt.c
@@ -1,101 +0,0 @@
-/*
- * Erik Arjan Hendriks <hendriks@lanl.gov>
- * Copyright (C) 2000 Scyld.
- * Copyright (C) 2000 Scyld Computing Corporation
- * Copyright (C) 2001 University of California. LA-CC Number 01-67.
- * Copyright (C) 2005 Nick.Barker9@btinternet.com
- * Copyright (C) 2007 coresystems GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-
-
-/* Declare a temporary global descriptor table -
- * necessary because the core part of the bios
- * no longer sets up any 16 bit segments
- */
-
-__asm__ (
- /* pointer to original gdt */
- " .globl gdtarg\n"
- "gdtarg: \n"
- " .word gdt_limit \n"
-#ifdef COREBOOT_V2
- " .long gdt \n"
-#else
- " .long gdtptr \n"
-#endif
-
- /* compute the table limit */
- "__mygdt_limit = __mygdt_end - __mygdt - 1 \n"
- " .globl __mygdtaddr\n"
- "__mygdtaddr: \n"
- " .word __mygdt_limit \n"
- " .long __mygdt \n"
-
- " .globl __mygdt\n"
- "__mygdt: \n"
- /* selgdt 0, unused */
- " .word 0x0000, 0x0000 \n"
- " .byte 0x00, 0x00, 0x00, 0x00 \n"
-
- /* selgdt 8, unused */
- " .word 0x0000, 0x0000 \n"
- " .byte 0x00, 0x00, 0x00, 0x00 \n"
-
- /* selgdt 0x10, flat code segment */
- " .word 0xffff, 0x0000 \n"
- " .byte 0x00, 0x9b, 0xcf, 0x00 \n"
-
- /* selgdt 0x18, flat data segment */
- " .word 0xffff, 0x0000 \n"
- " .byte 0x00, 0x93, 0xcf, 0x00 \n"
-
- /* selgdt 0x20, unused */
- " .word 0x0000, 0x0000 \n"
- " .byte 0x00, 0x00, 0x00, 0x00 \n"
-
- /* selgdt 0x28 16-bit 64k code at 0x00000000 */
- " .word 0xffff, 0x0000 \n"
- " .byte 0, 0x9a, 0, 0 \n"
-
- /* selgdt 0x30 16-bit 64k data at 0x00000000 */
- " .word 0xffff, 0x0000 \n"
- " .byte 0, 0x92, 0, 0 \n"
-
- "__mygdt_end: \n"
-
-#ifndef COREBOOT_V2
- /* FIXME: This does probably not belong here */
- " .globl idtarg\n"
- "idtarg:\n"
- " .word _idt_end - _idt - 1\n" /* limit */
- " .long _idt\n"
- " .word 0\n"
- "_idt:\n"
- " .fill 20, 8, 0\n" // # idt is unitiailzed
- "_idt_end:\n"
-#endif
-
- /* Declare a pointer to where our idt is going to be i.e. at mem zero */
- " .globl __myidt\n"
- "__myidt: \n"
- /* 16-bit limit */
- " .word 1023 \n"
- /* 24-bit base */
- " .long 0 \n"
- " .word 0 \n"
-);
diff --git a/util/x86emu/x86.c b/util/x86emu/x86.c
new file mode 100644
index 000000000000..8f7204d45805
--- /dev/null
+++ b/util/x86emu/x86.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2009 coresystems GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <device/pci.h>
+#include <string.h>
+
+#ifdef COREBOOT_V2
+#include <arch/io.h>
+#include <console/console.h>
+#define printk(x...) do_printk(x)
+#else
+#include <console.h>
+#endif
+
+#define REALMODE_BASE ((void *)0x500)
+
+struct realmode_idt {
+ u16 offset, cs;
+};
+
+struct eregs {
+ uint32_t eax, ecx, edx, ebx, esp, ebp, esi, edi;
+ uint32_t vector;
+ uint32_t error_code;
+ uint32_t eip;
+ uint32_t cs;
+ uint32_t eflags;
+};
+
+void x86_exception(struct eregs *info);
+
+extern unsigned char __idt_handler, __idt_handler_size;
+extern unsigned char __realmode_code, __realmode_code_size;
+extern unsigned char __run_optionrom, __run_interrupt;
+
+void (*run_optionrom)(u32 devfn) = (void *)&__run_optionrom;
+void (*vga_enable_console)(void) = (void *)&__run_interrupt;
+
+int (*intXX_handler[256])(struct eregs *regs) = { NULL };
+
+static int intXX_exception_handler(struct eregs *regs)
+{
+ printk(BIOS_INFO, "Oops, exception %d while executing option rom\n",
+ regs->vector);
+ x86_exception(regs); // Call coreboot exception handler
+
+ return 0; // Never returns?
+}
+
+static int intXX_unknown_handler(struct eregs *regs)
+{
+ printk(BIOS_INFO, "Unsupported software interrupt #0x%x\n",
+ regs->vector);
+
+ return -1;
+}
+
+int int12_handler(struct eregs *regs);
+int int15_handler(struct eregs *regs);
+int int1a_handler(struct eregs *regs);
+
+static void setup_interrupt_handlers(void)
+{
+ int i;
+
+ /* The first 16 intXX functions are not BIOS services,
+ * but the CPU-generated exceptions ("hardware interrupts")
+ */
+ for (i = 0; i < 0x10; i++)
+ intXX_handler[i] = &intXX_exception_handler;
+
+ /* Mark all other intXX calls as unknown first */
+ for (i = 0x10; i < 0x100; i++)
+ intXX_handler[i] = &intXX_unknown_handler;
+
+ /* Now set the default functions that are actually
+ * needed to initialize the option roms. This is very
+ * slick, as it allows us to implement mainboard specific
+ * interrupt handlers, such as the int15
+ */
+ intXX_handler[0x12] = &int12_handler;
+ intXX_handler[0x15] = &int15_handler;
+ intXX_handler[0x1a] = &int1a_handler;
+}
+
+static void write_idt_stub(void *target, u8 intnum)
+{
+ unsigned char *codeptr;
+ codeptr = (unsigned char *) target;
+ memcpy(codeptr, &__idt_handler, (size_t)&__idt_handler_size);
+ codeptr[3] = intnum; /* modify int# in the code stub. */
+}
+
+static void setup_realmode_idt(void)
+{
+ struct realmode_idt *idts = (struct realmode_idt *) 0;
+ int i;
+
+ /* Copy IDT stub code for each interrupt. This might seem wasteful
+ * but it is really simple
+ */
+ for (i = 0; i < 256; i++) {
+ idts[i].cs = 0;
+ idts[i].offset = 0x1000 + (i * (u32)&__idt_handler_size);
+ write_idt_stub((void *)((u32 )idts[i].offset), i);
+ }
+
+ /* Many option ROMs use the hard coded interrupt entry points in the
+ * system bios. So install them at the known locations.
+ * Only need int10 so far.
+ */
+
+ /* int42 is the relocated int10 */
+ write_idt_stub((void *)0xff065, 0x42);
+}
+
+void run_bios(struct device *dev, unsigned long addr)
+{
+ int i;
+
+ /* clear vga bios data area */
+ for (i = 0x400; i < 0x500; i++) {
+ *(unsigned char *) i = 0;
+ }
+
+ /* Set up C interrupt handlers */
+ setup_interrupt_handlers();
+
+ /* Setting up realmode IDT */
+ setup_realmode_idt();
+
+ memcpy(REALMODE_BASE, &__realmode_code, (size_t)&__realmode_code_size);
+ printk(BIOS_SPEW, "Real mode stub @%p: %d bytes\n", REALMODE_BASE,
+ (u32)&__realmode_code_size);
+
+ printk(BIOS_DEBUG, "Calling Option Rom...\n");
+ run_optionrom((dev->bus->secondary << 8) | dev->path.pci.devfn);
+}
+
+int __attribute__((regparm(0))) interrupt_handler(u32 intnumber,
+ u32 gsfs, u32 dses,
+ u32 edi, u32 esi,
+ u32 ebp, u32 esp,
+ u32 ebx, u32 edx,
+ u32 ecx, u32 eax,
+ u32 cs_ip, u16 stackflags)
+{
+ u32 ip;
+ u32 cs;
+ u32 flags;
+ int ret = -1;
+ struct eregs reg_info;
+
+ ip = cs_ip & 0xffff;
+ cs = cs_ip >> 16;
+ flags = stackflags;
+
+ printk(BIOS_DEBUG, "oprom: INT# 0x%x\n", intnumber);
+ printk(BIOS_DEBUG, "oprom: eax: %08x ebx: %08x ecx: %08x edx: %08x\n",
+ eax, ebx, ecx, edx);
+ printk(BIOS_DEBUG, "oprom: ebp: %08x esp: %08x edi: %08x esi: %08x\n",
+ ebp, esp, edi, esi);
+ printk(BIOS_DEBUG, "oprom: ip: %04x cs: %04x flags: %08x\n",
+ ip, cs, flags);
+
+ // Fetch arguments from the stack and put them into
+ // a structure that we want to pass on to our sub interrupt
+ // handlers.
+ reg_info = (struct eregs) {
+ .eax=eax,
+ .ecx=ecx,
+ .edx=edx,
+ .ebx=ebx,
+ .esp=esp,
+ .ebp=ebp,
+ .esi=esi,
+ .edi=edi,
+ .vector=intnumber,
+ .error_code=0, // ??
+ .eip=ip,
+ .cs=cs,
+ .eflags=flags // ??
+ };
+
+ // Call the interrupt handler for this int#
+ ret = intXX_handler[intnumber](&reg_info);
+
+ // Put registers back on the stack. The assembler code
+ // will later pop them.
+ // What happens here is that we force (volatile!) changing
+ // the values of the parameters of this function. We do this
+ // because we know that they stay alive on the stack after
+ // we leave this function. Don't say this is bollocks.
+ *(volatile u32 *)&eax = reg_info.eax;
+ *(volatile u32 *)&ecx = reg_info.ecx;
+ *(volatile u32 *)&edx = reg_info.edx;
+ *(volatile u32 *)&ebx = reg_info.ebx;
+ *(volatile u32 *)&esi = reg_info.esi;
+ *(volatile u32 *)&edi = reg_info.edi;
+ flags = reg_info.eflags;
+
+ /* Pass errors back to our caller via the CARRY flag */
+ if (ret) {
+ printk(BIOS_DEBUG,"error!\n");
+ flags |= 1; // error: set carry
+ }else{
+ flags &= ~1; // no error: clear carry
+ }
+ *(volatile u16 *)&stackflags = flags;
+
+ return ret;
+}
+
diff --git a/util/x86emu/x86_asm.S b/util/x86emu/x86_asm.S
new file mode 100644
index 000000000000..817a5c3d3334
--- /dev/null
+++ b/util/x86emu/x86_asm.S
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2009 coresystems GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#define REALMODE_BASE 0x500
+#define RELOCATED(x) (x - __realmode_code + REALMODE_BASE)
+
+/* CR0 bits */
+#define PE (1 << 0)
+
+/* This is the intXX interrupt handler stub code. It gets copied
+ * to the IDT and to some fixed addresses in the F segment. Before
+ * the code can used, it gets patched up by the C function copying
+ * it: byte 3 (the $0 in movb $0, %al) is overwritten with the int#.
+ */
+
+ .code16
+ .globl __idt_handler
+__idt_handler:
+ pushal
+ movb $0, %al /* This instruction gets modified */
+ ljmp $0, $__interrupt_handler_16bit
+ .globl __idt_handler_size
+__idt_handler_size = ( . - __idt_handler)
+
+
+/* In order to be independent of coreboot's position in RAM
+ * we relocate a part of the code to the low megabyte, so the
+ * CPU can use it in real-mode. This code lives at __realmode_code.
+ */
+ .globl __realmode_code
+__realmode_code:
+
+/* Realmode IDT pointer structure. */
+ .globl __realmode_idt
+__realmode_idt = RELOCATED(.)
+ .word 1023 /* 16-bit limit */
+ .long 0 /* 24-bit base */
+ .word 0
+
+/* Preserve old stack */
+__stack = RELOCATED(.)
+ .long 0
+
+ .code32
+ .globl __run_optionrom
+__run_optionrom = RELOCATED(.)
+ /* save all registers to the stack */
+ pushal
+
+ /* Move the protected mode stack to a safe place */
+ mov %esp, __stack
+
+ /* Get devfn into %ecx */
+ movl %esp, %ebp
+ // FIXME: Should this function be called with regparm=0?
+ movl 8(%ebp), %ecx
+
+ /* Clear interrupts before making a
+ * transition to Real Mode
+ */
+ cli
+
+ /* Activate the right segment descriptor real mode. */
+ ljmp $0x28, $RELOCATED(1f)
+1:
+.code16
+ /* 16 bit code from here on... */
+
+ /* Load the segment registers w/ properly configured
+ * segment descriptors. They will retain these
+ * configurations (limits, writability, etc.) once
+ * protected mode is turned off.
+ */
+ mov $0x30, %ax
+ mov %ax, %ds
+ mov %ax, %es
+ mov %ax, %fs
+ mov %ax, %gs
+ mov %ax, %ss
+
+ /* Turn off protection */
+ movl %cr0, %eax
+ andl $~PE, %eax
+ movl %eax, %cr0
+
+ /* Now really going into real mode */
+ ljmp $0, $RELOCATED(1f)
+1:
+ /* Setup a stack: Put the stack at the end of page zero.
+ * That way we can easily share it between real and
+ * protected, since the 16-bit ESP at segment 0 will
+ * work for any case. */
+ mov $0x0, %ax
+ mov %ax, %ss
+ movl $0x1000, %eax
+ movl %eax, %esp
+
+ /* Load our 16 it idt */
+ xor %ax, %ax
+ mov %ax, %ds
+ lidt __realmode_idt
+
+ /* All critical work is done, allow interrupts again */
+ sti
+
+ /* Set all segments to 0x0000, ds to 0x0040 */
+ mov %ax, %es
+ mov %ax, %fs
+ mov %ax, %gs
+ mov $0x40, %ax
+ mov %ax, %ds
+ mov %cx, %ax // restore ax
+
+ /* ************************************ */
+ // TODO this will not work for non-VGA option ROMs
+ /* run VGA BIOS at 0xc000:0003 */
+ lcall $0xc000, $0x0003
+ /* ************************************ */
+
+ /* Clear interrupts before making a
+ * transition to Protected Mode
+ */
+ cli
+
+ /* If we got here, just about done.
+ * Need to get back to protected mode
+ */
+ movl %cr0, %eax
+ orl $PE, %eax
+ movl %eax, %cr0
+
+ /* Now that we are in protected mode
+ * jump to a 32 bit code segment.
+ */
+ data32 ljmp $0x10, $RELOCATED(1f)
+1:
+ .code32
+ movw $0x18, %ax
+ mov %ax, %ds
+ mov %ax, %es
+ mov %ax, %fs
+ mov %ax, %gs
+ mov %ax, %ss
+
+ /* restore proper idt */
+ lidt idtarg
+
+ /* All critical work is done, allow interrupts again */
+ sti
+
+ /* and exit */
+ mov __stack, %esp
+ popal
+ ret
+
+ .globl __run_interrupt
+__run_interrupt = RELOCATED(.)
+
+ /* paranoia -- does ecx get saved? not sure. This is
+ * the easiest safe thing to do. */
+ pushal
+ /* save the stack */
+ mov %esp, __stack
+
+
+ /* Clear interrupts before making a
+ * transition to Real Mode
+ */
+ cli
+
+ /* This configures CS properly for real mode. */
+ ljmp $0x28, $RELOCATED(1f)
+1:
+ .code16 /* 16 bit code from here on... */
+
+ // DEBUG
+ movb $0xec, %al
+ outb %al, $0x80
+
+ /* Load the segment registers w/ properly configured segment
+ * descriptors. They will retain these configurations (limits,
+ * writability, etc.) once protected mode is turned off.
+ */
+ mov $0x30, %ax
+ mov %ax, %ds
+ mov %ax, %es
+ mov %ax, %fs
+ mov %ax, %gs
+ mov %ax, %ss
+
+ /* Turn off protected mode */
+ movl %cr0, %eax
+ andl $~PE, %eax
+ movl %eax, %cr0
+
+ /* Now really going into real mode */
+ data32 ljmp $0, $RELOCATED(1f)
+1:
+
+ /* put the stack at the end of page zero.
+ * that way we can easily share it between real and protected,
+ * since the 16-bit ESP at segment 0 will work for any case.
+ */
+ /* setup a stack */
+ mov $0x0, %ax
+ mov %ax, %ss
+ movl $0x1000, %eax
+ movl %eax, %esp
+
+ /* Load 16-bit intXX IDT */
+ xor %ax, %ax
+ mov %ax, %ds
+ lidt __realmode_idt
+
+ /* All critical work is done, allow interrupts again */
+ sti
+
+ /* Set all segments to 0x0000 */
+ mov %ax, %ds
+ mov %ax, %es
+ mov %ax, %fs
+ mov %ax, %gs
+
+ /* Call VGA BIOS int10 function 0x4f14 to enable main console
+ * Epia-M does not always autosence the main console so forcing
+ * it on is good.
+ */
+
+ /* Ask VGA option rom to enable main console */
+ movw $0x4f14,%ax
+ movw $0x8003,%bx
+ movw $1, %cx
+ movw $0, %dx
+ movw $0, %di
+ int $0x10
+
+ /* Clear interrupts before making a
+ * transition to Protected Mode
+ */
+ cli
+
+ /* Ok, the job is done, now go back to protected mode coreboot */
+ movl %cr0, %eax
+ orl $PE, %eax
+ movl %eax, %cr0
+
+ /* Now that we are in protected mode jump to a 32-bit code segment. */
+ data32 ljmp $0x10, $RELOCATED(1f)
+1:
+ .code32
+ movw $0x18, %ax
+ mov %ax, %ds
+ mov %ax, %es
+ mov %ax, %fs
+ mov %ax, %gs
+ mov %ax, %ss
+
+ /* restore coreboot's 32-bit IDT */
+ lidt idtarg
+
+ /* All critical work is done, allow interrupts again */
+ sti
+
+ /* Exit */
+ mov __stack, %esp
+ popal
+ ret
+
+/* This is the 16-bit interrupt entry point called by the IDT stub code.
+ * Before this code code is called, %eax is pushed to the stack, and the
+ * interrupt number is loaded into %al
+ */
+ .code16
+__interrupt_handler_16bit = RELOCATED(.)
+ push %ds
+ push %es
+ push %fs
+ push %gs
+
+ /* Clean up the interrupt number. We could have done this in the stub,
+ * but it would have cost 2 more bytes per stub entry.
+ */
+ andl $0xff, %eax
+ pushl %eax /* ... and make it the first parameter */
+
+ /* Clear interrupts before making a
+ * transition to Protected Mode
+ */
+ cli
+
+ /* Switch to protected mode */
+ movl %cr0, %eax
+ orl $PE, %eax
+ movl %eax, %cr0
+
+ /* ... and jump to a 32 bit code segment. */
+ data32 ljmp $0x10, $RELOCATED(1f)
+1:
+ .code32
+ movw $0x18, %ax
+ mov %ax, %ds
+ mov %ax, %es
+ mov %ax, %fs
+ mov %ax, %gs
+ mov %ax, %ss
+
+ lidt idtarg
+
+ /* All critical work is done, allow interrupts again */
+ sti
+
+ /* Call the C interrupt handler */
+ movl $interrupt_handler, %eax
+ call *%eax
+
+ /* Now return to real mode ... */
+ ljmp $0x28, $RELOCATED(1f)
+1:
+ .code16
+ /* Load the segment registers with properly configured segment
+ * descriptors. They will retain these configurations (limits,
+ * writability, etc.) once protected mode is turned off.
+ */
+ mov $0x30, %ax
+ mov %ax, %ds
+ mov %ax, %es
+ mov %ax, %fs
+ mov %ax, %gs
+ mov %ax, %ss
+
+ /* Clear interrupts before making a
+ * transition to Real Mode
+ */
+ cli
+
+ /* Disable Protected Mode */
+ movl %cr0, %eax
+ andl $~PE, %eax
+ movl %eax, %cr0
+
+ /* Now really going into real mode */
+ ljmp $0, $RELOCATED(1f)
+1:
+ /* Restore real-mode stack segment */
+ mov $0x0, %ax
+ mov %ax, %ss
+
+ /* Restore 16-bit IDT */
+ xor %ax, %ax
+ mov %ax, %ds
+ lidt __realmode_idt
+
+ /* All critical work is done, allow interrupts again */
+ sti
+
+ /* Set up our segment registers to segment 0x0000 */
+ mov %ax, %es
+ mov %ax, %fs
+ mov %ax, %gs
+ mov $0x40, %ax
+ mov %ax, %ds
+
+ /* Restore all registers, including those
+ * manipulated by the C handler
+ */
+ popl %eax
+ pop %gs
+ pop %fs
+ pop %es
+ pop %ds
+ popal
+ iret
+
+ .globl __realmode_code_size
+__realmode_code_size = (. - __realmode_code)
+
+ .code32
diff --git a/util/x86emu/x86_interrupts.c b/util/x86emu/x86_interrupts.c
new file mode 100644
index 000000000000..056ea2fa3696
--- /dev/null
+++ b/util/x86emu/x86_interrupts.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2001 Ronald G. Minnich
+ * Copyright (C) 2005 Nick.Barker9@btinternet.com
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <device/pci_ops.h>
+#include <string.h>
+#ifdef COREBOOT_V2
+#include <console/console.h>
+#include <arch/io.h>
+#define printk(x...) do_printk(x)
+#else
+#include <console.h>
+#include <io.h>
+#endif
+
+struct eregs {
+ uint32_t eax, ecx, edx, ebx, esp, ebp, esi, edi;
+ uint32_t vector;
+ uint32_t error_code;
+ uint32_t eip;
+ uint32_t cs;
+ uint32_t eflags;
+};
+
+enum {
+ CHECK = 0xb001,
+ FINDDEV = 0xb102,
+ READCONFBYTE = 0xb108,
+ READCONFWORD = 0xb109,
+ READCONFDWORD = 0xb10a,
+ WRITECONFBYTE = 0xb10b,
+ WRITECONFWORD = 0xb10c,
+ WRITECONFDWORD = 0xb10d
+};
+
+// errors go in AH. Just set these up so that word assigns
+// will work. KISS.
+enum {
+ PCIBIOS_NODEV = 0x8600,
+ PCIBIOS_BADREG = 0x8700
+};
+
+int int12_handler(struct eregs *regs)
+{
+ regs->eax = 64 * 1024;
+ return 0;
+}
+
+int int1a_handler(struct eregs *regs)
+{
+ unsigned short func = (unsigned short) regs->eax;
+ int retval = 0;
+ unsigned short devid, vendorid, devfn;
+ /* Use short to get rid of gabage in upper half of 32-bit register */
+ short devindex;
+ unsigned char bus;
+ struct device *dev;
+
+ switch(func) {
+ case CHECK:
+ regs->edx = 0x4350;
+ regs->ecx = 0x2049;
+ retval = 0;
+ break;
+ case FINDDEV:
+ {
+ devid = regs->ecx;
+ vendorid = regs->edx;
+ devindex = regs->esi;
+ dev = 0;
+#ifdef COREBOOT_V2
+ while ((dev = dev_find_device(vendorid, devid, dev))) {
+#else
+ while ((dev = dev_find_pci_device(vendorid, devid, dev))) {
+#endif
+ if (devindex <= 0)
+ break;
+ devindex--;
+ }
+ if (dev) {
+ unsigned short busdevfn;
+ regs->eax = 0;
+ // busnum is an unsigned char;
+ // devfn is an int, so we mask it off.
+ busdevfn = (dev->bus->secondary << 8)
+ | (dev->path.pci.devfn & 0xff);
+ printk(BIOS_DEBUG, "0x%x: return 0x%x\n", func, busdevfn);
+ regs->ebx = busdevfn;
+ retval = 0;
+ } else {
+ regs->eax = PCIBIOS_NODEV;
+ retval = -1;
+ }
+ }
+ break;
+ case READCONFDWORD:
+ case READCONFWORD:
+ case READCONFBYTE:
+ case WRITECONFDWORD:
+ case WRITECONFWORD:
+ case WRITECONFBYTE:
+ {
+ unsigned long dword;
+ unsigned short word;
+ unsigned char byte;
+ unsigned char reg;
+
+ devfn = regs->ebx & 0xff;
+ bus = regs->ebx >> 8;
+ reg = regs->edi;
+ dev = dev_find_slot(bus, devfn);
+ if (! dev) {
+ printk(BIOS_DEBUG, "0x%x: BAD DEVICE bus %d devfn 0x%x\n", func, bus, devfn);
+ // idiots. the pcibios guys assumed you'd never pass a bad bus/devfn!
+ regs->eax = PCIBIOS_BADREG;
+ retval = -1;
+ }
+ switch(func) {
+ case READCONFBYTE:
+ byte = pci_read_config8(dev, reg);
+ regs->ecx = byte;
+ break;
+ case READCONFWORD:
+ word = pci_read_config16(dev, reg);
+ regs->ecx = word;
+ break;
+ case READCONFDWORD:
+ dword = pci_read_config32(dev, reg);
+ regs->ecx = dword;
+ break;
+ case WRITECONFBYTE:
+ byte = regs->ecx;
+ pci_write_config8(dev, reg, byte);
+ break;
+ case WRITECONFWORD:
+ word = regs->ecx;
+ pci_write_config16(dev, reg, word);
+ break;
+ case WRITECONFDWORD:
+ dword = regs->ecx;
+ pci_write_config32(dev, reg, dword);
+ break;
+ }
+
+ if (retval)
+ retval = PCIBIOS_BADREG;
+ printk(BIOS_DEBUG, "0x%x: bus %d devfn 0x%x reg 0x%x val 0x%x\n",
+ func, bus, devfn, reg, regs->ecx);
+ regs->eax = 0;
+ retval = 0;
+ }
+ break;
+ default:
+ printk(BIOS_ERR, "UNSUPPORTED PCIBIOS FUNCTION 0x%x\n", func);
+ break;
+ }
+
+ return retval;
+}
+
+int int15_handler(struct eregs *regs)
+{
+ int res = -1;
+
+ /* This int15 handler is VIA Tech. specific. Other chipsets need other
+ * handlers. The right way to do this is to move this handler code into
+ * the mainboard or northbridge code.
+ */
+ switch (regs->eax & 0xffff) {
+ case 0x5f19:
+ break;
+ case 0x5f18:
+ regs->eax = 0x5f;
+ // MCLK = 133, 32M frame buffer, 256 M main memory
+ regs->ebx = 0x545;
+ regs->ecx = 0x060;
+ res = 0;
+ break;
+ case 0x5f00:
+ regs->eax = 0x8600;
+ break;
+ case 0x5f01:
+ regs->eax = 0x5f;
+ regs->ecx = (regs->ecx & 0xffffff00 ) | 2; // panel type = 2 = 1024 * 768
+ res = 0;
+ break;
+ case 0x5f02:
+ regs->eax = 0x5f;
+ regs->ebx = (regs->ebx & 0xffff0000) | 2;
+ regs->ecx = (regs->ecx & 0xffff0000) | 0x401; // PAL + crt only
+ regs->edx = (regs->edx & 0xffff0000) | 0; // TV Layout - default
+ res = 0;
+ break;
+ case 0x5f0f:
+ regs->eax = 0x860f;
+ break;
+ /* And now Intel IGD code */
+#define BOOT_DISPLAY_CRT (1 << 0)
+#define BOOT_DISPLAY_TV (1 << 1)
+#define BOOT_DISPLAY_EFP (1 << 2)
+#define BOOT_DISPLAY_LCD (1 << 3)
+#define BOOT_DISPLAY_CRT2 (1 << 4)
+#define BOOT_DISPLAY_TV2 (1 << 5)
+#define BOOT_DISPLAY_EFP2 (1 << 6)
+#define BOOT_DISPLAY_LCD2 (1 << 7)
+
+ case 0x5f35:
+ regs->eax = 0x5f;
+ regs->ecx = BOOT_DISPLAY_LCD|BOOT_DISPLAY_CRT;
+ res = 0;
+ break;
+ case 0x5f40:
+ regs->eax = 0x5f;
+ regs->ecx = 3; // This is mainboard specific
+ printk(BIOS_DEBUG, "DISPLAY=%x\n", regs->ecx);
+ res = 0;
+ break;
+ default:
+ printk(BIOS_DEBUG, "Unknown INT15 function %04x!\n",
+ regs->eax & 0xffff);
+ }
+
+ return res;
+}
+