Author: stepan Date: 2009-05-29 15:09:57 +0200 (Fri, 29 May 2009) New Revision: 1167
Added: coreboot-v3/util/x86emu/x86.c coreboot-v3/util/x86emu/x86_asm.S coreboot-v3/util/x86emu/x86_interrupts.c Modified: coreboot-v3/util/x86emu/Config.lb coreboot-v3/util/x86emu/Makefile coreboot-v3/util/x86emu/biosemu.c coreboot-v3/util/x86emu/vm86.c coreboot-v3/util/x86emu/vm86_gdt.c Log: 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
Modified: coreboot-v3/util/x86emu/Config.lb =================================================================== --- coreboot-v3/util/x86emu/Config.lb 2009-05-09 23:36:20 UTC (rev 1166) +++ coreboot-v3/util/x86emu/Config.lb 2009-05-29 13:09:57 UTC (rev 1167) @@ -6,8 +6,9 @@ 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
Modified: coreboot-v3/util/x86emu/Makefile =================================================================== --- coreboot-v3/util/x86emu/Makefile 2009-05-09 23:36:20 UTC (rev 1166) +++ coreboot-v3/util/x86emu/Makefile 2009-05-29 13:09:57 UTC (rev 1167) @@ -37,7 +37,7 @@ #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)
Modified: coreboot-v3/util/x86emu/biosemu.c =================================================================== --- coreboot-v3/util/x86emu/biosemu.c 2009-05-09 23:36:20 UTC (rev 1166) +++ coreboot-v3/util/x86emu/biosemu.c 2009-05-29 13:09:57 UTC (rev 1167) @@ -213,6 +213,7 @@ ret = run_bios_int(num);
} + #if 0 #define SYS_BIOS 0xf0000 /* @@ -386,7 +387,7 @@ pushw(X86_SS); pushw(X86_SP + 2);
-#ifndef NO_TRACE +#ifdef DEBUG //X86EMU_trace_on(); #endif
Modified: coreboot-v3/util/x86emu/vm86.c =================================================================== --- coreboot-v3/util/x86emu/vm86.c 2009-05-09 23:36:20 UTC (rev 1166) +++ coreboot-v3/util/x86emu/vm86.c 2009-05-29 13:09:57 UTC (rev 1167) @@ -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; -}
Modified: coreboot-v3/util/x86emu/vm86_gdt.c =================================================================== --- coreboot-v3/util/x86emu/vm86_gdt.c 2009-05-09 23:36:20 UTC (rev 1166) +++ coreboot-v3/util/x86emu/vm86_gdt.c 2009-05-29 13:09:57 UTC (rev 1167) @@ -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" -);
Added: coreboot-v3/util/x86emu/x86.c =================================================================== --- coreboot-v3/util/x86emu/x86.c (rev 0) +++ coreboot-v3/util/x86emu/x86.c 2009-05-29 13:09:57 UTC (rev 1167) @@ -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](®_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; +} +
Added: coreboot-v3/util/x86emu/x86_asm.S =================================================================== --- coreboot-v3/util/x86emu/x86_asm.S (rev 0) +++ coreboot-v3/util/x86emu/x86_asm.S 2009-05-29 13:09:57 UTC (rev 1167) @@ -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
Added: coreboot-v3/util/x86emu/x86_interrupts.c =================================================================== --- coreboot-v3/util/x86emu/x86_interrupts.c (rev 0) +++ coreboot-v3/util/x86emu/x86_interrupts.c 2009-05-29 13:09:57 UTC (rev 1167) @@ -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; +} +