diff options
Diffstat (limited to 'arch/x86/lib')
-rw-r--r-- | arch/x86/lib/Makefile | 59 | ||||
-rw-r--r-- | arch/x86/lib/bios.S | 531 | ||||
-rw-r--r-- | arch/x86/lib/bios.h | 92 | ||||
-rw-r--r-- | arch/x86/lib/bios_pci.S | 413 | ||||
-rw-r--r-- | arch/x86/lib/bios_setup.c | 241 | ||||
-rw-r--r-- | arch/x86/lib/board.c | 480 | ||||
-rw-r--r-- | arch/x86/lib/bootm.c | 97 | ||||
-rw-r--r-- | arch/x86/lib/interrupts.c | 161 | ||||
-rw-r--r-- | arch/x86/lib/pcat_interrupts.c | 132 | ||||
-rw-r--r-- | arch/x86/lib/pcat_timer.c | 107 | ||||
-rw-r--r-- | arch/x86/lib/pci.c | 150 | ||||
-rw-r--r-- | arch/x86/lib/pci_type1.c | 66 | ||||
-rw-r--r-- | arch/x86/lib/realmode.c | 96 | ||||
-rw-r--r-- | arch/x86/lib/realmode_switch.S | 221 | ||||
-rw-r--r-- | arch/x86/lib/timer.c | 110 | ||||
-rw-r--r-- | arch/x86/lib/video.c | 229 | ||||
-rw-r--r-- | arch/x86/lib/video_bios.c | 222 | ||||
-rw-r--r-- | arch/x86/lib/zimage.c | 296 |
18 files changed, 3703 insertions, 0 deletions
diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile new file mode 100644 index 0000000000..71e94f76f3 --- /dev/null +++ b/arch/x86/lib/Makefile @@ -0,0 +1,59 @@ +# +# (C) Copyright 2002-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# 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 $(TOPDIR)/config.mk + +LIB = $(obj)lib$(ARCH).o + +SOBJS-y += bios.o +SOBJS-y += bios_pci.o +SOBJS-y += realmode_switch.o + +COBJS-y += bios_setup.o +COBJS-y += board.o +COBJS-y += bootm.o +COBJS-y += interrupts.o +COBJS-$(CONFIG_SYS_PCAT_INTERRUPTS) += pcat_interrupts.o +COBJS-$(CONFIG_SYS_GENERIC_TIMER) += pcat_timer.o +COBJS-$(CONFIG_PCI) += pci.o +COBJS-$(CONFIG_PCI) += pci_type1.o +COBJS-y += realmode.o +COBJS-y += timer.o +COBJS-y += video_bios.o +COBJS-y += video.o +COBJS-y += zimage.o + +SRCS := $(SOBJS-y:.o=.S) $(COBJS-y:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS-y) $(COBJS-y)) + +$(LIB): $(obj).depend $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/arch/x86/lib/bios.S b/arch/x86/lib/bios.S new file mode 100644 index 0000000000..660a244394 --- /dev/null +++ b/arch/x86/lib/bios.S @@ -0,0 +1,531 @@ +/* + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +/* + * Based on msbios.c from rolo 1.6: + *---------------------------------------------------------------------- + * (C) Copyright 2000 + * Sysgo Real-Time Solutions GmbH + * Klein-Winternheim, Germany + *---------------------------------------------------------------------- + */ + +#include "bios.h" + +/* + * During it's initialization phase, before switching to protected + * mode, the Linux Kernel makes a few BIOS calls. This won't work + * if the board does not have a BIOS. + * + * This is a very minimalisic BIOS that supplies just enough + * functionality to keep the Linux Kernel happy. It is NOT + * a general purpose replacement for a real BIOS !! + */ + +.section .bios, "ax" +.code16 +.org 0 + /* a call to f000:0 should warmboot */ + jmp realmode_reset + +.globl rm_int00 +.hidden rm_int00 +.type rm_int00, @function +rm_int00: + pushw $0 + jmp any_interrupt16 +.globl rm_int01 +.hidden rm_int01 +.type rm_int01, @function +rm_int01: + pushw $1 + jmp any_interrupt16 +.globl rm_int02 +.hidden rm_int02 +.type rm_int02, @function +rm_int02: + pushw $2 + jmp any_interrupt16 +.globl rm_int03 +.hidden rm_int03 +.type rm_int03, @function +rm_int03: + pushw $3 + jmp any_interrupt16 +.globl rm_int04 +.hidden rm_int04 +.type rm_int04, @function +rm_int04: + pushw $4 + jmp any_interrupt16 +.globl rm_int05 +.hidden rm_int05 +.type rm_int05, @function +rm_int05: + pushw $5 + jmp any_interrupt16 +.globl rm_int06 +.hidden rm_int06 +.type rm_int06, @function +rm_int06: + pushw $6 + jmp any_interrupt16 +.globl rm_int07 +.hidden rm_int07 +.type rm_int07, @function +rm_int07: + pushw $7 + jmp any_interrupt16 +.globl rm_int08 +.hidden rm_int08 +.type rm_int08, @function +rm_int08: + pushw $8 + jmp any_interrupt16 +.globl rm_int09 +.hidden rm_int09 +.type rm_int09, @function +rm_int09: + pushw $9 + jmp any_interrupt16 +.globl rm_int0a +.hidden rm_int0a +.type rm_int0a, @function +rm_int0a: + pushw $10 + jmp any_interrupt16 +.globl rm_int0b +.hidden rm_int0b +.type rm_int0b, @function +rm_int0b: + pushw $11 + jmp any_interrupt16 +.globl rm_int0c +.hidden rm_int0c +.type rm_int0c, @function +rm_int0c: + pushw $12 + jmp any_interrupt16 +.globl rm_int0d +.hidden rm_int0d +.type rm_int0d, @function +rm_int0d: + pushw $13 + jmp any_interrupt16 +.globl rm_int0e +.hidden rm_int0e +.type rm_int0e, @function +rm_int0e: + pushw $14 + jmp any_interrupt16 +.globl rm_int0f +.hidden rm_int0f +.type rm_int0f, @function +rm_int0f: + pushw $15 + jmp any_interrupt16 +.globl rm_int10 +.hidden rm_int10 +.type rm_int10, @function +rm_int10: + pushw $16 + jmp any_interrupt16 +.globl rm_int11 +.hidden rm_int11 +.type rm_int11, @function +rm_int11: + pushw $17 + jmp any_interrupt16 +.globl rm_int12 +.hidden rm_int12 +.type rm_int12, @function +rm_int12: + pushw $18 + jmp any_interrupt16 +.globl rm_int13 +.hidden rm_int13 +.type rm_int13, @function +rm_int13: + pushw $19 + jmp any_interrupt16 +.globl rm_int14 +.hidden rm_int14 +.type rm_int14, @function +rm_int14: + pushw $20 + jmp any_interrupt16 +.globl rm_int15 +.hidden rm_int15 +.type rm_int15, @function +rm_int15: + pushw $21 + jmp any_interrupt16 +.globl rm_int16 +.hidden rm_int16 +.type rm_int16, @function +rm_int16: + pushw $22 + jmp any_interrupt16 +.globl rm_int17 +.hidden rm_int17 +.type rm_int17, @function +rm_int17: + pushw $23 + jmp any_interrupt16 +.globl rm_int18 +.hidden rm_int18 +.type rm_int18, @function +rm_int18: + pushw $24 + jmp any_interrupt16 +.globl rm_int19 +.hidden rm_int19 +.type rm_int19, @function +rm_int19: + pushw $25 + jmp any_interrupt16 +.globl rm_int1a +.hidden rm_int1a +.type rm_int1a, @function +rm_int1a: + pushw $26 + jmp any_interrupt16 +.globl rm_int1b +.hidden rm_int1b +.type rm_int1b, @function +rm_int1b: + pushw $27 + jmp any_interrupt16 +.globl rm_int1c +.hidden rm_int1c +.type rm_int1c, @function +rm_int1c: + pushw $28 + jmp any_interrupt16 +.globl rm_int1d +.hidden rm_int1d +.type rm_int1d, @function +rm_int1d: + pushw $29 + jmp any_interrupt16 +.globl rm_int1e +.hidden rm_int1e +.type rm_int1e, @function +rm_int1e: + pushw $30 + jmp any_interrupt16 +.globl rm_int1f +.hidden rm_int1f +.type rm_int1f, @function +rm_int1f: + pushw $31 + jmp any_interrupt16 +.globl rm_def_int +.hidden rm_def_int +.type rm_def_int, @function +rm_def_int: + iret + + + /* + * All interrupt jumptable entries jump to here + * after pushing the interrupt vector number onto the + * stack. + */ +any_interrupt16: + MAKE_BIOS_STACK + +gs movw OFFS_VECTOR(%bp), %ax + cmpw $0x10, %ax + je Lint_10h + cmpw $0x11, %ax + je Lint_11h + cmpw $0x12, %ax + je Lint_12h + cmpw $0x13, %ax + je Lint_13h + cmpw $0x15, %ax + je Lint_15h + cmpw $0x16, %ax + je Lint_16h + cmpw $0x1a, %ax + je Lint_1ah + movw $0xffff, %ax + jmp Lout +Lint_10h: /* VGA BIOS services */ + call bios_10h + jmp Lout +Lint_11h: + call bios_11h + jmp Lout +Lint_12h: + call bios_12h + jmp Lout +Lint_13h: /* BIOS disk services */ + call bios_13h + jmp Lout +Lint_15h: /* Misc. BIOS services */ + call bios_15h + jmp Lout +Lint_16h: /* keyboard services */ + call bios_16h + jmp Lout +Lint_1ah: /* PCI bios */ + call bios_1ah + jmp Lout +Lout: + cmpw $0, %ax + je Lhandeled + + /* Insert code for unhandeled INTs here. + * + * ROLO prints a message to the console + * (we could do that but then we're in 16bit mode + * so we'll have to get back into 32bit mode + * to use the console I/O routines (if we do this + * we shuls make int 0x10 and int 0x16 work as well)) + */ +Lhandeled: + RESTORE_CALLERS_STACK + addw $2,%sp /* dump vector number */ + iret /* return from interrupt */ + + +/* + ************************************************************ + * BIOS interrupt 10h -- VGA services + ************************************************************ + */ +bios_10h: +gs movw OFFS_AX(%bp), %ax + shrw $8, %ax + cmpw $0x3, %ax + je Lcur_pos + cmpw $0xf, %ax + je Lvid_state + cmpw $0x12, %ax + je Lvid_cfg + movw $0xffff, %ax + ret +Lcur_pos: /* Read Cursor Position and Size */ +gs movw $0, OFFS_CX(%bp) +gs movw $0, OFFS_DX(%bp) + xorw %ax, %ax + ret +Lvid_state: /* Get Video State */ +gs movw $(80 << 8|0x03), OFFS_AX(%bp) /* 80 columns, 80x25, 16 colors */ +gs movw $0, OFFS_BX(%bp) + xorw %ax, %ax + ret +Lvid_cfg: /* Video Subsystem Configuration (EGA/VGA) */ +gs movw $0x10, OFFS_BX(%bp) /* indicate CGA/MDA/HGA */ + xorw %ax, %ax + ret + + +/* + ************************************************************ + * BIOS interrupt 11h -- Equipment determination + ************************************************************ + */ + +bios_11h: +cs movw bios_equipment, %ax +gs movw %ax, OFFS_AX(%bp) + xorw %ax, %ax + ret + + +/* + ************************************************************ + * BIOS interrupt 12h -- Get Memory Size + ************************************************************ + */ +bios_12h: +cs movw ram_in_64kb_chunks, %ax + cmpw $0xa, %ax + ja b12_more_than_640k + shlw $6, %ax + jmp b12_return +b12_more_than_640k: + movw $0x280, %ax +b12_return: +gs movw %ax, OFFS_AX(%bp) /* return number of kilobytes in ax */ + +gs movw OFFS_FLAGS(%bp), %ax + andw $0xfffe, %ax /* clear carry -- function succeeded */ +gs movw %ax, OFFS_FLAGS(%bp) + + xorw %ax, %ax + ret + + +/* + ************************************************************ + * BIOS interrupt 13h -- Disk services + ************************************************************ + */ +bios_13h: +gs movw OFFS_AX(%bp), %ax + shrw $8, %ax + cmpw $0x15, %ax + je Lfunc_15h + movw $0xffff, %ax + ret +Lfunc_15h: +gs movw OFFS_AX(%bp), %ax + andw $0xff, %ax /* return AH=0->drive not present */ +gs movw %ax, OFFS_AX(%bp) + xorw %ax, %ax + ret + + +/* + *********************************************************** + * BIOS interrupt 15h -- Miscellaneous services + *********************************************************** + */ +bios_15h: +gs movw OFFS_AX(%bp), %ax + shrw $8, %ax + cmpw $0xc0, %ax + je Lfunc_c0h + cmpw $0xe8, %ax + je Lfunc_e8h + cmpw $0x88, %ax + je Lfunc_88h + movw $0xffff, %ax + ret + +Lfunc_c0h: /* Return System Configuration Parameters (PS2 only) */ +gs movw OFFS_FLAGS(%bp), %ax + orw $1, %ax /* return carry -- function not supported */ +gs movw %ax, OFFS_FLAGS(%bp) + xorw %ax, %ax + ret + +Lfunc_e8h: +gs movw OFFS_AX(%bp), %ax + andw $0xff, %ax + cmpw $1, %ax + je Lfunc_e801h +gs movw OFFS_FLAGS(%bp), %ax + orw $1, %ax /* return carry -- function not supported */ +gs movw %ax, OFFS_FLAGS(%bp) + xorw %ax, %ax + ret + +Lfunc_e801h: /* Get memory size for >64M Configurations */ +cs movw ram_in_64kb_chunks, %ax + cmpw $0x100, %ax + ja e801_more_than_16mb + shlw $6, %ax /* multiply by 64 */ + subw $0x400, %ax /* 1st meg does not count */ + +gs movw %ax, OFFS_AX(%bp) /* return memory size between 1M and 16M in 1kb chunks in AX and CX */ +gs movw %ax, OFFS_CX(%bp) +gs movw $0, OFFS_BX(%bp) /* set BX and DX to 0*/ +gs movw $0, OFFS_DX(%bp) +gs movw OFFS_FLAGS(%bp), %ax + andw $0xfffe, %ax /* clear carry -- function succeeded */ +gs movw %ax, OFFS_FLAGS(%bp) + xorw %ax, %ax + ret + +e801_more_than_16mb: + subw $0x100, %ax /* subtract 16MB */ + +gs movw $0x3c00, OFFS_AX(%bp) /* return 0x3c00 (16MB-1MB) in AX and CX */ +gs movw $0x3c00, OFFS_CX(%bp) +gs movw %ax, OFFS_BX(%bp) /* set BX and DX to number of 64kb chunks above 16MB */ +gs movw %ax, OFFS_DX(%bp) + +gs movw OFFS_FLAGS(%bp), %ax + andw $0xfffe, %ax /* clear carry -- function succeeded */ +gs movw %ax, OFFS_FLAGS(%bp) + xorw %ax, %ax + ret + +Lfunc_88h: +cs movw ram_in_64kb_chunks, %ax + cmpw $0x100, %ax + jna b88_not_more_than16 + movw $0x100, %ax +b88_not_more_than16: + shlw $6, %ax + subw $0x400, %ax /* 1st meg does not count */ + +gs movw %ax, OFFS_AX(%bp) /* return number of kilobytes between 16MB and 16MB in ax */ + +gs movw OFFS_FLAGS(%bp), %ax + andw $0xfffe, %ax /* clear carry -- function succeeded */ +gs movw %ax, OFFS_FLAGS(%bp) + + xorw %ax, %ax + ret + + +/* + ************************************************************ + * BIOS interrupt 16h -- keyboard services + ************************************************************ + */ +bios_16h: +gs movw OFFS_AX(%bp), %ax + shrw $8, %ax + cmpw $0x03, %ax + je Lfunc_03h + movw $0xffff, %ax + ret +Lfunc_03h: + xorw %ax, %ax /* do nothing -- function not supported */ + ret + +/* + ************************************************************ + * BIOS interrupt 1ah -- PCI bios + ************************************************************ + */ +bios_1ah: +gs movw OFFS_AX(%bp), %ax + cmpb $0xb1, %ah + je Lfunc_b1h + movw $0xffff, %ax + ret +Lfunc_b1h: + call realmode_pci_bios + xorw %ax, %ax /* do nothing -- function not supported */ + ret + + +.globl ram_in_64kb_chunks +.hidden ram_in_64kb_chunks +.type ram_in_64kb_chunks, @function +ram_in_64kb_chunks: + .word 0 + +.globl bios_equipment +.hidden bios_equipment +.type bios_equipment, @function +bios_equipment: + .word 0 diff --git a/arch/x86/lib/bios.h b/arch/x86/lib/bios.h new file mode 100644 index 0000000000..3c8d61a6f4 --- /dev/null +++ b/arch/x86/lib/bios.h @@ -0,0 +1,92 @@ +/* + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +#ifndef _BIOS_H_ +#define _BIOS_H_ + +#define OFFS_ES 0 /* 16bit */ +#define OFFS_GS 2 /* 16bit */ +#define OFFS_DS 4 /* 16bit */ +#define OFFS_EDI 6 /* 32bit */ +#define OFFS_DI 6 /* low 16 bits of EDI */ +#define OFFS_ESI 10 /* 32bit */ +#define OFFS_SI 10 /* low 16 bits of ESI */ +#define OFFS_EBP 14 /* 32bit */ +#define OFFS_BP 14 /* low 16 bits of EBP */ +#define OFFS_ESP 18 /* 32bit */ +#define OFFS_SP 18 /* low 16 bits of ESP */ +#define OFFS_EBX 22 /* 32bit */ +#define OFFS_BX 22 /* low 16 bits of EBX */ +#define OFFS_BL 22 /* low 8 bits of BX */ +#define OFFS_BH 23 /* high 8 bits of BX */ +#define OFFS_EDX 26 /* 32bit */ +#define OFFS_DX 26 /* low 16 bits of EBX */ +#define OFFS_DL 26 /* low 8 bits of BX */ +#define OFFS_DH 27 /* high 8 bits of BX */ +#define OFFS_ECX 30 /* 32bit */ +#define OFFS_CX 30 /* low 16 bits of EBX */ +#define OFFS_CL 30 /* low 8 bits of BX */ +#define OFFS_CH 31 /* high 8 bits of BX */ +#define OFFS_EAX 34 /* 32bit */ +#define OFFS_AX 34 /* low 16 bits of EBX */ +#define OFFS_AL 34 /* low 8 bits of BX */ +#define OFFS_AH 35 /* high 8 bits of BX */ +#define OFFS_VECTOR 38 /* 16bit */ +#define OFFS_IP 40 /* 16bit */ +#define OFFS_CS 42 /* 16bit */ +#define OFFS_FLAGS 44 /* 16bit */ + +#define SEGMENT 0x40 +#define STACK 0x800 /* stack at 0x40:0x800 -> 0x800 */ + +/* save general registers */ +/* save some segments */ +/* save callers stack segment .. */ +/* ... in gs */ + /* setup my segments */ + /* setup BIOS stackpointer */ + +#define MAKE_BIOS_STACK \ + pushal; \ + pushw %ds; \ + pushw %gs; \ + pushw %es; \ + pushw %ss; \ + popw %gs; \ + movw $SEGMENT, %ax; \ + movw %ax, %ds; \ + movw %ax, %es; \ + movw %ax, %ss; \ + movw %sp, %bp; \ + movw $STACK, %sp + +#define RESTORE_CALLERS_STACK \ + pushw %gs; /* restore callers stack segment */ \ + popw %ss; \ + movw %bp, %sp; /* restore stackpointer */ \ + popw %es; /* restore segment selectors */ \ + popw %gs; \ + popw %ds; \ + popal /* restore GP registers */ + +#endif diff --git a/arch/x86/lib/bios_pci.S b/arch/x86/lib/bios_pci.S new file mode 100644 index 0000000000..9e412e5e4c --- /dev/null +++ b/arch/x86/lib/bios_pci.S @@ -0,0 +1,413 @@ +/* + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, daniel@omicron.se + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +/* + * x86 realmode assembly implementation of a PCI BIOS + * for platforms that use one PCI hose and configuration + * access type 1. (The common case for low-end PC's) + */ + +#include "bios.h" + +#define PCI_BIOS_DEBUG + +.section .bios, "ax" +.code16 +.globl realmode_pci_bios_call_entry +.hidden realmode_pci_bios_call_entry +.type realmode_pci_bios_call_entry, @function +realmode_pci_bios_call_entry: + MAKE_BIOS_STACK + call realmode_pci_bios + RESTORE_CALLERS_STACK + ret + + +.globl realmode_pci_bios +realmode_pci_bios: +gs movw OFFS_AX(%bp), %ax + cmpb $1, %al + je pci_bios_present + cmpb $2, %al + je pci_bios_find_device + cmpb $3, %al + je pci_bios_find_class + cmpb $6, %al + je pci_bios_generate_special_cycle + cmpb $8, %al + je pci_bios_read_cfg_byte + cmpb $9, %al + je pci_bios_read_cfg_word + cmpb $10, %al + je pci_bios_read_cfg_dword + cmpb $11, %al + je pci_bios_write_cfg_byte + cmpb $12, %al + je pci_bios_write_cfg_word + cmpb $13, %al + je pci_bios_write_cfg_dword + cmpb $14, %al + je pci_bios_get_irq_routing + cmpb $15, %al + je pci_bios_set_irq + jmp unknown_function + +/*****************************************************************************/ + +pci_bios_present: +#ifdef PCI_BIOS_DEBUG +cs incl num_pci_bios_present +#endif + movl $0x20494350, %eax +gs movl %eax, OFFS_EDX(%bp) + movb $0x01, %al +gs movb %al, OFFS_AL(%bp) /* We support cfg type 1 */ + movw $0x0210, %ax /* version 2.10 */ +gs movw %ax, OFFS_BX(%bp) +cs movb pci_last_bus, %al /* last bus number */ +gs movb %al, OFFS_CL(%bp) + jmp clear_carry + +/*****************************************************************************/ + +/* device 0-31, function 0-7 */ +pci_bios_find_device: +#ifdef PCI_BIOS_DEBUG +cs incl num_pci_bios_find_device +#endif +gs movw OFFS_CX(%bp), %di + shll $16, %edi +gs movw OFFS_DX(%bp), %di /* edi now holds device in upper 16 + * bits and vendor in lower 16 bits */ +gs movw OFFS_SI(%bp), %si + xorw %bx, %bx /* start at bus 0 dev 0 function 0 */ +pfd_loop: + xorw %ax, %ax /* dword 0 is vendor/device */ + call __pci_bios_select_register + movw $0xcfc, %dx + inl %dx, %eax + cmpl %edi, %eax /* our device ? */ + je pfd_found_one +pfd_next_dev: + /* check for multi function devices */ + movw %bx, %ax + andw $3, %ax + jnz pfd_function_not_zero + movw $0x000c, %ax + call __pci_bios_select_register + movw $0xcfe, %dx + inb %dx, %al + andb $0x80, %al + jz pfd_not_multi_function +pfd_function_not_zero: + incw %bx /* next function, overflows in to + * device number, then bus number */ + jmp pfd_check_bus + +pfd_not_multi_function: + andw $0xfff8, %bx /* remove function bits */ + addw $0x0008, %bx /* next device, overflows in to bus number */ +pfd_check_bus: +cs movb pci_last_bus, %ah + cmpb %ah, %bh + ja pfd_not_found + jmp pfd_loop +pfd_found_one: + decw %si + js pfd_done + jmp pfd_next_dev + +pfd_done: +gs movw %bx, OFFS_BX(%bp) + jmp clear_carry + +pfd_not_found: + movb $0x86, %ah /* device not found */ + jmp set_carry + +/*****************************************************************************/ + +pci_bios_find_class: +#ifdef PCI_BIOS_DEBUG +cs incl num_pci_bios_find_class +#endif +gs movl OFFS_ECX(%bp), %edi + andl $0x00ffffff, %edi /* edi now holds class-code in lower 24 bits */ +gs movw OFFS_SI(%bp), %si + xorw %bx, %bx /* start at bus 0 dev 0 function 0 */ +pfc_loop: + movw $8, %ax /* dword 8 is class-code high 24bits */ + call __pci_bios_select_register + movw $0xcfc, %dx + inl %dx, %eax + shrl $8, %eax + andl $0x00ffffff, %eax + cmpl %edi, %eax /* our device ? */ + je pfc_found_one +pfc_next_dev: + /* check for multi function devices */ + andw $3, %bx + jnz pfc_function_not_zero + movw $0x000c, %ax + call __pci_bios_select_register + movw $0xcfe, %dx + inb %dx, %al + andb $0x80, %al + jz pfc_not_multi_function +pfc_function_not_zero: + incw %bx /* next function, overflows in to + * device number, then bus number */ + jmp pfc_check_bus + +pfc_not_multi_function: + andw $0xfff8, %bx /* remove function bits */ + addw $0x0008, %bx /* next device, overflows in to bus number */ +pfc_check_bus: +cs movb pci_last_bus, %ah + cmpb %ah, %bh + ja pfc_not_found + jmp pfc_loop +pfc_found_one: + decw %si + js pfc_done + jmp pfc_next_dev + +pfc_done: +gs movw %bx, OFFS_BX(%bp) + jmp clear_carry + +pfc_not_found: + movb $0x86, %ah /* device not found */ + jmp set_carry + +/*****************************************************************************/ + +pci_bios_generate_special_cycle: +#ifdef PCI_BIOS_DEBUG +cs incl num_pci_bios_generate_special_cycle +#endif + movb $0x81, %ah /* function not supported */ + jmp set_carry + +/*****************************************************************************/ + +pci_bios_read_cfg_byte: +#ifdef PCI_BIOS_DEBUG +cs incl num_pci_bios_read_cfg_byte +#endif + call pci_bios_select_register +gs movw OFFS_DI(%bp), %dx + andw $3, %dx + addw $0xcfc, %dx + inb %dx, %al +gs movb %al, OFFS_CL(%bp) + jmp clear_carry + +/*****************************************************************************/ + +pci_bios_read_cfg_word: +#ifdef PCI_BIOS_DEBUG +cs incl num_pci_bios_read_cfg_word +#endif + call pci_bios_select_register +gs movw OFFS_DI(%bp), %dx + andw $2, %dx + addw $0xcfc, %dx + inw %dx, %ax +gs movw %ax, OFFS_CX(%bp) + jmp clear_carry + + +/*****************************************************************************/ + +pci_bios_read_cfg_dword: +#ifdef PCI_BIOS_DEBUG +cs incl num_pci_bios_read_cfg_dword +#endif + call pci_bios_select_register + movw $0xcfc, %dx + inl %dx, %eax +gs movl %eax, OFFS_ECX(%bp) + jmp clear_carry + +/*****************************************************************************/ + +pci_bios_write_cfg_byte: +#ifdef PCI_BIOS_DEBUG +cs incl num_pci_bios_write_cfg_byte +#endif + call pci_bios_select_register +gs movw OFFS_DI(%bp), %dx +gs movb OFFS_CL(%bp), %al + andw $3, %dx + addw $0xcfc, %dx + outb %al, %dx + jmp clear_carry + +/*****************************************************************************/ + +pci_bios_write_cfg_word: +#ifdef PCI_BIOS_DEBUG +cs incl num_pci_bios_write_cfg_word +#endif + call pci_bios_select_register +gs movw OFFS_DI(%bp), %dx +gs movw OFFS_CX(%bp), %ax + andw $2, %dx + addw $0xcfc, %dx + outw %ax, %dx + jmp clear_carry + +/*****************************************************************************/ + +pci_bios_write_cfg_dword: +#ifdef PCI_BIOS_DEBUG +cs incl num_pci_bios_write_cfg_dword +#endif + call pci_bios_select_register +gs movl OFFS_ECX(%bp), %eax + movw $0xcfc, %dx + outl %eax, %dx + jmp clear_carry + +/*****************************************************************************/ + +pci_bios_get_irq_routing: +#ifdef PCI_BIOS_DEBUG +cs incl num_pci_bios_get_irq_routing +#endif + movb $0x81, %ah /* function not supported */ + jmp set_carry + +/*****************************************************************************/ + +pci_bios_set_irq: +#ifdef PCI_BIOS_DEBUG +cs incl num_pci_bios_set_irq +#endif + movb $0x81, %ah /* function not supported */ + jmp set_carry + +/*****************************************************************************/ + +unknown_function: +#ifdef PCI_BIOS_DEBUG +cs incl num_pci_bios_unknown_function +#endif + movb $0x81, %ah /* function not supported */ + jmp set_carry + +/*****************************************************************************/ + +pci_bios_select_register: +gs movw OFFS_BX(%bp), %bx +gs movw OFFS_DI(%bp), %ax +/* destroys eax, dx */ +__pci_bios_select_register: /* BX holds device id, AX holds register index */ + pushl %ebx + andl $0xfc, %eax + andl $0xffff, %ebx + shll $8, %ebx + orl %ebx, %eax + orl $0x80000000, %eax + movw $0xcf8, %dx + outl %eax, %dx + popl %ebx + ret + + +clear_carry: +gs movw OFFS_FLAGS(%bp), %ax + andw $0xfffe, %ax /* clear carry -- function succeeded */ +gs movw %ax, OFFS_FLAGS(%bp) + xorw %ax, %ax +gs movb %ah, OFFS_AH(%bp) + ret + +set_carry: +gs movb %ah, OFFS_AH(%bp) +gs movw OFFS_FLAGS(%bp), %ax + orw $1, %ax /* return carry -- function not supported */ +gs movw %ax, OFFS_FLAGS(%bp) + movw $-1, %ax + ret + +/*****************************************************************************/ + +.globl pci_last_bus +pci_last_bus: + .byte 0 + +#ifdef PCI_BIOS_DEBUG +.globl num_pci_bios_present +num_pci_bios_present: + .long 0 + +.globl num_pci_bios_find_device +num_pci_bios_find_device: + .long 0 + +.globl num_pci_bios_find_class +num_pci_bios_find_class: + .long 0 + +.globl num_pci_bios_generate_special_cycle +num_pci_bios_generate_special_cycle: + .long 0 + +.globl num_pci_bios_read_cfg_byte +num_pci_bios_read_cfg_byte: + .long 0 + +.globl num_pci_bios_read_cfg_word +num_pci_bios_read_cfg_word: + .long 0 + +.globl num_pci_bios_read_cfg_dword +num_pci_bios_read_cfg_dword: + .long 0 + +.globl num_pci_bios_write_cfg_byte +num_pci_bios_write_cfg_byte: + .long 0 + +.globl num_pci_bios_write_cfg_word +num_pci_bios_write_cfg_word: + .long 0 + +.globl num_pci_bios_write_cfg_dword +num_pci_bios_write_cfg_dword: + .long 0 + +.globl num_pci_bios_get_irq_routing +num_pci_bios_get_irq_routing: + .long 0 + +.globl num_pci_bios_set_irq +num_pci_bios_set_irq: + .long 0 + +.globl num_pci_bios_unknown_function +num_pci_bios_unknown_function: + .long 0 +#endif diff --git a/arch/x86/lib/bios_setup.c b/arch/x86/lib/bios_setup.c new file mode 100644 index 0000000000..6949b35069 --- /dev/null +++ b/arch/x86/lib/bios_setup.c @@ -0,0 +1,241 @@ +/* + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +/* + * Partly based on msbios.c from rolo 1.6: + *---------------------------------------------------------------------- + * (C) Copyright 2000 + * Sysgo Real-Time Solutions GmbH + * Klein-Winternheim, Germany + *---------------------------------------------------------------------- + */ + +#include <common.h> +#include <pci.h> +#include <asm/realmode.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define NUMVECTS 256 + +#define BIOS_DATA ((char*)0x400) +#define BIOS_DATA_SIZE 256 +#define BIOS_BASE ((char*)0xf0000) +#define BIOS_CS 0xf000 + +extern ulong __bios_start; +extern ulong __bios_size; + +/* these are defined in a 16bit segment and needs + * to be accessed with the RELOC_16_xxxx() macros below + */ +extern u16 ram_in_64kb_chunks; +extern u16 bios_equipment; +extern u8 pci_last_bus; + +extern void *rm_int00; +extern void *rm_int01; +extern void *rm_int02; +extern void *rm_int03; +extern void *rm_int04; +extern void *rm_int05; +extern void *rm_int06; +extern void *rm_int07; +extern void *rm_int08; +extern void *rm_int09; +extern void *rm_int0a; +extern void *rm_int0b; +extern void *rm_int0c; +extern void *rm_int0d; +extern void *rm_int0e; +extern void *rm_int0f; +extern void *rm_int10; +extern void *rm_int11; +extern void *rm_int12; +extern void *rm_int13; +extern void *rm_int14; +extern void *rm_int15; +extern void *rm_int16; +extern void *rm_int17; +extern void *rm_int18; +extern void *rm_int19; +extern void *rm_int1a; +extern void *rm_int1b; +extern void *rm_int1c; +extern void *rm_int1d; +extern void *rm_int1e; +extern void *rm_int1f; +extern void *rm_def_int; + +extern void *realmode_reset; +extern void *realmode_pci_bios_call_entry; + +static int set_jmp_vector(int entry_point, void *target) +{ + if (entry_point & ~0xffff) { + return -1; + } + + if (((u32)target-0xf0000) & ~0xffff) { + return -1; + } + printf("set_jmp_vector: 0xf000:%04x -> %p\n", + entry_point, target); + + /* jmp opcode */ + writeb(0xea, 0xf0000 + entry_point); + + /* offset */ + writew(((u32)target-0xf0000), 0xf0000 + entry_point + 1); + + /* segment */ + writew(0xf000, 0xf0000 + entry_point + 3); + + return 0; +} + + +/* + ************************************************************ + * Install an interrupt vector + ************************************************************ + */ + +static void setvector(int vector, u16 segment, void *handler) +{ + u16 *ptr = (u16*)(vector*4); + ptr[0] = ((u32)handler - (segment << 4))&0xffff; + ptr[1] = segment; + +#if 0 + printf("setvector: int%02x -> %04x:%04x\n", + vector, ptr[1], ptr[0]); +#endif +} + +#define RELOC_16_LONG(seg, off) *(u32*)(seg << 4 | (u32)&off) +#define RELOC_16_WORD(seg, off) *(u16*)(seg << 4 | (u32)&off) +#define RELOC_16_BYTE(seg, off) *(u8*)(seg << 4 | (u32)&off) + +int bios_setup(void) +{ + ulong bios_start = (ulong)&__bios_start + gd->reloc_off; + ulong bios_size = (ulong)&__bios_size; + + static int done=0; + int vector; +#ifdef CONFIG_PCI + struct pci_controller *pri_hose; +#endif + if (done) { + return 0; + } + done = 1; + + if (bios_size > 65536) { + printf("BIOS too large (%ld bytes, max is 65536)\n", + bios_size); + return -1; + } + + memcpy(BIOS_BASE, (void*)bios_start, bios_size); + + /* clear bda */ + memset(BIOS_DATA, 0, BIOS_DATA_SIZE); + + /* enter some values to the bda */ + writew(0x3f8, BIOS_DATA); /* com1 addr */ + writew(0x2f8, BIOS_DATA+2); /* com2 addr */ + writew(0x3e8, BIOS_DATA+4); /* com3 addr */ + writew(0x2e8, BIOS_DATA+6); /* com4 addr */ + writew(0x278, BIOS_DATA+8); /* lpt1 addr */ + /* + * The kernel wants to read the base memory size + * from 40:13. Put a zero there to avoid an error message + */ + writew(0, BIOS_DATA+0x13); /* base memory size */ + + + /* setup realmode interrupt vectors */ + for (vector = 0; vector < NUMVECTS; vector++) { + setvector(vector, BIOS_CS, &rm_def_int); + } + + setvector(0x00, BIOS_CS, &rm_int00); + setvector(0x01, BIOS_CS, &rm_int01); + setvector(0x02, BIOS_CS, &rm_int02); + setvector(0x03, BIOS_CS, &rm_int03); + setvector(0x04, BIOS_CS, &rm_int04); + setvector(0x05, BIOS_CS, &rm_int05); + setvector(0x06, BIOS_CS, &rm_int06); + setvector(0x07, BIOS_CS, &rm_int07); + setvector(0x08, BIOS_CS, &rm_int08); + setvector(0x09, BIOS_CS, &rm_int09); + setvector(0x0a, BIOS_CS, &rm_int0a); + setvector(0x0b, BIOS_CS, &rm_int0b); + setvector(0x0c, BIOS_CS, &rm_int0c); + setvector(0x0d, BIOS_CS, &rm_int0d); + setvector(0x0e, BIOS_CS, &rm_int0e); + setvector(0x0f, BIOS_CS, &rm_int0f); + setvector(0x10, BIOS_CS, &rm_int10); + setvector(0x11, BIOS_CS, &rm_int11); + setvector(0x12, BIOS_CS, &rm_int12); + setvector(0x13, BIOS_CS, &rm_int13); + setvector(0x14, BIOS_CS, &rm_int14); + setvector(0x15, BIOS_CS, &rm_int15); + setvector(0x16, BIOS_CS, &rm_int16); + setvector(0x17, BIOS_CS, &rm_int17); + setvector(0x18, BIOS_CS, &rm_int18); + setvector(0x19, BIOS_CS, &rm_int19); + setvector(0x1a, BIOS_CS, &rm_int1a); + setvector(0x1b, BIOS_CS, &rm_int1b); + setvector(0x1c, BIOS_CS, &rm_int1c); + setvector(0x1d, BIOS_CS, &rm_int1d); + setvector(0x1e, BIOS_CS, &rm_int1e); + setvector(0x1f, BIOS_CS, &rm_int1f); + + set_jmp_vector(0xfff0, &realmode_reset); + set_jmp_vector(0xfe6e, &realmode_pci_bios_call_entry); + + /* fill in data area */ + RELOC_16_WORD(0xf000, ram_in_64kb_chunks) = gd->ram_size >> 16; + RELOC_16_WORD(0xf000, bios_equipment) = 0; /* FixMe */ + + /* If we assume only one PCI hose, this PCI hose + * will own PCI bus #0, and the last PCI bus of + * that PCI hose will be the last PCI bus in the + * system. + * (This, ofcause break on multi hose systems, + * but our PCI BIOS only support one hose anyway) + */ +#ifdef CONFIG_PCI + pri_hose = pci_bus_to_hose(0); + if (NULL != pri_hose) { + /* fill in last pci bus number for use by the realmode + * PCI BIOS */ + RELOC_16_BYTE(0xf000, pci_last_bus) = pri_hose->last_busno; + } +#endif + return 0; +} diff --git a/arch/x86/lib/board.c b/arch/x86/lib/board.c new file mode 100644 index 0000000000..df54222211 --- /dev/null +++ b/arch/x86/lib/board.c @@ -0,0 +1,480 @@ +/* + * (C) Copyright 2008-2011 + * Graeme Russ, <graeme.russ@gmail.com> + * + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se> + * + * (C) Copyright 2002 + * Wolfgang Denk, DENX Software Engineering, <wd@denx.de> + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 <common.h> +#include <watchdog.h> +#include <command.h> +#include <stdio_dev.h> +#include <timestamp.h> +#include <version.h> +#include <malloc.h> +#include <net.h> +#include <ide.h> +#include <serial.h> +#include <asm/u-boot-x86.h> +#include <elf.h> + +#ifdef CONFIG_BITBANGMII +#include <miiphy.h> +#endif + +/* + * Pointer to initial global data area + * + * Here we initialize it. + */ +#undef XTRN_DECLARE_GLOBAL_DATA_PTR +#define XTRN_DECLARE_GLOBAL_DATA_PTR /* empty = allocate here */ +DECLARE_GLOBAL_DATA_PTR = (gd_t *) (CONFIG_SYS_INIT_GD_ADDR); + + +/* Exports from the Linker Script */ +extern ulong __text_start; +extern ulong __data_end; +extern ulong __rel_dyn_start; +extern ulong __rel_dyn_end; +extern ulong __bss_start; +extern ulong __bss_end; + +const char version_string[] = + U_BOOT_VERSION" (" U_BOOT_DATE " - " U_BOOT_TIME ")"; + +/************************************************************************ + * Init Utilities * + ************************************************************************ + * Some of this code should be moved into the core functions, + * or dropped completely, + * but let's get it working (again) first... + */ +static int init_baudrate (void) +{ + char tmp[64]; /* long enough for environment variables */ + int i = getenv_f("baudrate", tmp, 64); + + gd->baudrate = (i != 0) + ? (int) simple_strtoul (tmp, NULL, 10) + : CONFIG_BAUDRATE; + + return (0); +} + +static int display_banner (void) +{ + + printf ("\n\n%s\n\n", version_string); +/* + printf ("U-Boot code: %08lX -> %08lX data: %08lX -> %08lX\n" + " BSS: %08lX -> %08lX stack: %08lX -> %08lX\n", + i386boot_start, i386boot_romdata_start-1, + i386boot_romdata_dest, i386boot_romdata_dest+i386boot_romdata_size-1, + i386boot_bss_start, i386boot_bss_start+i386boot_bss_size-1, + i386boot_bss_start+i386boot_bss_size, + i386boot_bss_start+i386boot_bss_size+CONFIG_SYS_STACK_SIZE-1); + +*/ + + return (0); +} + +static int display_dram_config (void) +{ + int i; + + puts ("DRAM Configuration:\n"); + + for (i=0; i<CONFIG_NR_DRAM_BANKS; i++) { + printf ("Bank #%d: %08lx ", i, gd->bd->bi_dram[i].start); + print_size (gd->bd->bi_dram[i].size, "\n"); + } + + return (0); +} + +static void display_flash_config (ulong size) +{ + puts ("Flash: "); + print_size (size, "\n"); +} + +/* + * Breath some life into the board... + * + * Initialize an SMC for serial comms, and carry out some hardware + * tests. + * + * The first part of initialization is running from Flash memory; + * its main purpose is to initialize the RAM so that we + * can relocate the monitor code to RAM. + */ + +/* + * All attempts to come up with a "common" initialization sequence + * that works for all boards and architectures failed: some of the + * requirements are just _too_ different. To get rid of the resulting + * mess of board dependend #ifdef'ed code we now make the whole + * initialization sequence configurable to the user. + * + * The requirements for any new initalization function is simple: it + * receives a pointer to the "global data" structure as it's only + * argument, and returns an integer return code, where 0 means + * "continue" and != 0 means "fatal error, hang the system". + */ +typedef int (init_fnc_t) (void); + +static int calculate_relocation_address(void); +static int copy_uboot_to_ram(void); +static int clear_bss(void); +static int do_elf_reloc_fixups(void); + +init_fnc_t *init_sequence_f[] = { + cpu_init_f, + board_early_init_f, + env_init, + init_baudrate, + serial_init, + console_init_f, + dram_init_f, + calculate_relocation_address, + copy_uboot_to_ram, + clear_bss, + do_elf_reloc_fixups, + + NULL, +}; + +init_fnc_t *init_sequence_r[] = { + cpu_init_r, /* basic cpu dependent setup */ + board_early_init_r, /* basic board dependent setup */ + dram_init, /* configure available RAM banks */ + interrupt_init, /* set up exceptions */ + timer_init, + display_banner, + display_dram_config, + + NULL, +}; + +gd_t *gd; + +static int calculate_relocation_address(void) +{ + void *text_start = &__text_start; + void *bss_end = &__bss_end; + void *dest_addr; + ulong rel_offset; + + /* Calculate destination RAM Address and relocation offset */ + dest_addr = (void *)gd->ram_size; + dest_addr -= CONFIG_SYS_STACK_SIZE; + dest_addr -= (bss_end - text_start); + rel_offset = dest_addr - text_start; + + gd->start_addr_sp = gd->ram_size; + gd->relocaddr = (ulong)dest_addr; + gd->reloc_off = rel_offset; + + return 0; +} + +static int copy_uboot_to_ram(void) +{ + ulong *dst_addr = (ulong *)gd->relocaddr; + ulong *src_addr = (ulong *)&__text_start; + ulong *end_addr = (ulong *)&__data_end; + + while (src_addr < end_addr) + *dst_addr++ = *src_addr++; + + return 0; +} + +static int clear_bss(void) +{ + void *bss_start = &__bss_start; + void *bss_end = &__bss_end; + + ulong *dst_addr = (ulong *)(bss_start + gd->reloc_off); + ulong *end_addr = (ulong *)(bss_end + gd->reloc_off);; + + while (dst_addr < end_addr) + *dst_addr++ = 0x00000000; + + return 0; +} + +static int do_elf_reloc_fixups(void) +{ + Elf32_Rel *re_src = (Elf32_Rel *)(&__rel_dyn_start); + Elf32_Rel *re_end = (Elf32_Rel *)(&__rel_dyn_end); + + do { + if (re_src->r_offset >= CONFIG_SYS_TEXT_BASE) + if (*(Elf32_Addr *)(re_src->r_offset + gd->reloc_off) >= CONFIG_SYS_TEXT_BASE) + *(Elf32_Addr *)(re_src->r_offset + gd->reloc_off) += gd->reloc_off; + } while (re_src++ < re_end); + + return 0; +} + +/* Load U-Boot into RAM, initialize BSS, perform relocation adjustments */ +void board_init_f(ulong boot_flags) +{ + init_fnc_t **init_fnc_ptr; + + gd->flags = boot_flags; + + for (init_fnc_ptr = init_sequence_f; *init_fnc_ptr; ++init_fnc_ptr) { + if ((*init_fnc_ptr)() != 0) + hang(); + } + + gd->flags |= GD_FLG_RELOC; + + /* Enter the relocated U-Boot! */ + relocate_code(gd->start_addr_sp, gd, gd->relocaddr); + + /* NOTREACHED - relocate_code() does not return */ + while(1); +} + +void board_init_r(gd_t *id, ulong dest_addr) +{ + char *s; + ulong size; + static bd_t bd_data; + static gd_t gd_data; + init_fnc_t **init_fnc_ptr; + + show_boot_progress(0x21); + + /* Global data pointer is now writable */ + gd = &gd_data; + memcpy(gd, id, sizeof(gd_t)); + + /* compiler optimization barrier needed for GCC >= 3.4 */ + __asm__ __volatile__("": : :"memory"); + + gd->bd = &bd_data; + memset (gd->bd, 0, sizeof (bd_t)); + show_boot_progress(0x22); + + gd->baudrate = CONFIG_BAUDRATE; + + mem_malloc_init((((ulong)dest_addr - CONFIG_SYS_MALLOC_LEN)+3)&~3, + CONFIG_SYS_MALLOC_LEN); + + for (init_fnc_ptr = init_sequence_r; *init_fnc_ptr; ++init_fnc_ptr) { + if ((*init_fnc_ptr)() != 0) + hang (); + } + show_boot_progress(0x23); + +#ifdef CONFIG_SERIAL_MULTI + serial_initialize(); +#endif + /* configure available FLASH banks */ + size = flash_init(); + display_flash_config(size); + show_boot_progress(0x24); + + show_boot_progress(0x25); + + /* initialize environment */ + env_relocate (); + show_boot_progress(0x26); + + +#ifdef CONFIG_CMD_NET + /* IP Address */ + bd_data.bi_ip_addr = getenv_IPaddr ("ipaddr"); +#endif + +#if defined(CONFIG_PCI) + /* + * Do pci configuration + */ + pci_init(); +#endif + + show_boot_progress(0x27); + + + stdio_init (); + + jumptable_init (); + + /* Initialize the console (after the relocation and devices init) */ + console_init_r(); + +#ifdef CONFIG_MISC_INIT_R + /* miscellaneous platform dependent initialisations */ + misc_init_r(); +#endif + +#if defined(CONFIG_CMD_PCMCIA) && !defined(CONFIG_CMD_IDE) + WATCHDOG_RESET(); + puts ("PCMCIA:"); + pcmcia_init(); +#endif + +#if defined(CONFIG_CMD_KGDB) + WATCHDOG_RESET(); + puts("KGDB: "); + kgdb_init(); +#endif + + /* enable exceptions */ + enable_interrupts(); + show_boot_progress(0x28); + +#ifdef CONFIG_STATUS_LED + status_led_set (STATUS_LED_BOOT, STATUS_LED_BLINKING); +#endif + + udelay(20); + + set_timer (0); + + /* Initialize from environment */ + if ((s = getenv ("loadaddr")) != NULL) { + load_addr = simple_strtoul (s, NULL, 16); + } +#if defined(CONFIG_CMD_NET) + if ((s = getenv ("bootfile")) != NULL) { + copy_filename (BootFile, s, sizeof (BootFile)); + } +#endif + + WATCHDOG_RESET(); + +#if defined(CONFIG_CMD_IDE) + WATCHDOG_RESET(); + puts("IDE: "); + ide_init(); +#endif + +#if defined(CONFIG_CMD_SCSI) + WATCHDOG_RESET(); + puts("SCSI: "); + scsi_init(); +#endif + +#if defined(CONFIG_CMD_DOC) + WATCHDOG_RESET(); + puts("DOC: "); + doc_init(); +#endif + +#ifdef CONFIG_BITBANGMII + bb_miiphy_init(); +#endif +#if defined(CONFIG_CMD_NET) +#if defined(CONFIG_NET_MULTI) + WATCHDOG_RESET(); + puts("Net: "); +#endif + eth_initialize(gd->bd); +#endif + +#if ( defined(CONFIG_CMD_NET)) && (0) + WATCHDOG_RESET(); +# ifdef DEBUG + puts ("Reset Ethernet PHY\n"); +# endif + reset_phy(); +#endif + +#ifdef CONFIG_LAST_STAGE_INIT + WATCHDOG_RESET(); + /* + * Some parts can be only initialized if all others (like + * Interrupts) are up and running (i.e. the PC-style ISA + * keyboard). + */ + last_stage_init(); +#endif + + +#ifdef CONFIG_POST + post_run (NULL, POST_RAM | post_bootmode_get(0)); +#endif + + + show_boot_progress(0x29); + + /* main_loop() can return to retry autoboot, if so just run it again. */ + for (;;) { + main_loop(); + } + + /* NOTREACHED - no way out of command loop except booting */ +} + +void hang (void) +{ + puts ("### ERROR ### Please RESET the board ###\n"); + for (;;); +} + +unsigned long do_go_exec (ulong (*entry)(int, char * const []), int argc, char * const argv[]) +{ + unsigned long ret = 0; + char **argv_tmp; + + /* + * x86 does not use a dedicated register to pass the pointer to + * the global_data, so it is instead passed as argv[-1]. By using + * argv[-1], the called 'Application' can use the contents of + * argv natively. However, to safely use argv[-1] a new copy of + * argv is needed with the extra element + */ + argv_tmp = malloc(sizeof(char *) * (argc + 1)); + + if (argv_tmp) { + argv_tmp[0] = (char *)gd; + + memcpy(&argv_tmp[1], argv, (size_t)(sizeof(char *) * argc)); + + ret = (entry) (argc, &argv_tmp[1]); + free(argv_tmp); + } + + return ret; +} + +void setup_pcat_compatibility(void) + __attribute__((weak, alias("__setup_pcat_compatibility"))); + +void __setup_pcat_compatibility(void) +{ +} diff --git a/arch/x86/lib/bootm.c b/arch/x86/lib/bootm.c new file mode 100644 index 0000000000..a21a21f1f7 --- /dev/null +++ b/arch/x86/lib/bootm.c @@ -0,0 +1,97 @@ +/* + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * Copyright (C) 2001 Erik Mouw (J.A.K.Mouw@its.tudelft.nl) + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 <common.h> +#include <command.h> +#include <image.h> +#include <u-boot/zlib.h> +#include <asm/byteorder.h> +#include <asm/zimage.h> + +/*cmd_boot.c*/ +int do_bootm_linux(int flag, int argc, char * const argv[], bootm_headers_t *images) +{ + void *base_ptr; + ulong os_data, os_len; + image_header_t *hdr; + +#if defined(CONFIG_FIT) + const void *data; + size_t len; +#endif + + if ((flag != 0) && (flag != BOOTM_STATE_OS_GO)) + return 1; + + if (images->legacy_hdr_valid) { + hdr = images->legacy_hdr_os; + if (image_check_type (hdr, IH_TYPE_MULTI)) { + /* if multi-part image, we need to get first subimage */ + image_multi_getimg (hdr, 0, &os_data, &os_len); + } else { + /* otherwise get image data */ + os_data = image_get_data (hdr); + os_len = image_get_data_size (hdr); + } +#if defined(CONFIG_FIT) + } else if (images->fit_uname_os) { + ret = fit_image_get_data (images->fit_hdr_os, + images->fit_noffset_os, &data, &len); + if (ret) { + puts ("Can't get image data/size!\n"); + goto error; + } + os_data = (ulong)data; + os_len = (ulong)len; +#endif + } else { + puts ("Could not find kernel image!\n"); + goto error; + } + + base_ptr = load_zimage ((void*)os_data, os_len, + images->rd_start, images->rd_end - images->rd_start, 0); + + if (NULL == base_ptr) { + printf ("## Kernel loading failed ...\n"); + goto error; + + } + +#ifdef DEBUG + printf ("## Transferring control to Linux (at address %08x) ...\n", + (u32)base_ptr); +#endif + + /* we assume that the kernel is in place */ + printf("\nStarting kernel ...\n\n"); + + boot_zimage(base_ptr); + /* does not return */ + +error: + return 1; +} diff --git a/arch/x86/lib/interrupts.c b/arch/x86/lib/interrupts.c new file mode 100644 index 0000000000..a2c598f9a4 --- /dev/null +++ b/arch/x86/lib/interrupts.c @@ -0,0 +1,161 @@ +/* + * (C) Copyright 2009 + * Graeme Russ, <graeme.russ@gmail.com> + * + * (C) Copyright 2007 + * Daniel Hellstrom, Gaisler Research, <daniel@gaisler.com> + * + * (C) Copyright 2006 + * Detlev Zundel, DENX Software Engineering, <dzu@denx.de> + * + * (C) Copyright -2003 + * Wolfgang Denk, DENX Software Engineering, <wd@denx.de> + * + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se> + * + * (C) Copyright 2001 + * Josh Huber, Mission Critical Linux, Inc, <huber@mclx.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +/* + * This file contains the high-level API for the interrupt sub-system + * of the x86 port of U-Boot. Most of the functionality has been + * shamelessly stolen from the leon2 / leon3 ports of U-Boot. + * Daniel Hellstrom, Detlev Zundel, Wolfgang Denk and Josh Huber are + * credited for the corresponding work on those ports. The original + * interrupt handling routines for the x86 port were written by + * Daniel Engström + */ + +#include <common.h> +#include <asm/interrupt.h> + +struct irq_action { + interrupt_handler_t *handler; + void *arg; + unsigned int count; +}; + +static struct irq_action irq_handlers[CONFIG_SYS_NUM_IRQS] = { {0} }; +static int spurious_irq_cnt = 0; +static int spurious_irq = 0; + +void irq_install_handler(int irq, interrupt_handler_t *handler, void *arg) +{ + int status; + + if (irq < 0 || irq >= CONFIG_SYS_NUM_IRQS) { + printf("irq_install_handler: bad irq number %d\n", irq); + return; + } + + if (irq_handlers[irq].handler != NULL) + printf("irq_install_handler: 0x%08lx replacing 0x%08lx\n", + (ulong) handler, + (ulong) irq_handlers[irq].handler); + + status = disable_interrupts (); + + irq_handlers[irq].handler = handler; + irq_handlers[irq].arg = arg; + irq_handlers[irq].count = 0; + + unmask_irq(irq); + + if (status) + enable_interrupts(); + + return; +} + +void irq_free_handler(int irq) +{ + int status; + + if (irq < 0 || irq >= CONFIG_SYS_NUM_IRQS) { + printf("irq_free_handler: bad irq number %d\n", irq); + return; + } + + status = disable_interrupts (); + + mask_irq(irq); + + irq_handlers[irq].handler = NULL; + irq_handlers[irq].arg = NULL; + + if (status) + enable_interrupts(); + + return; +} + +void do_irq(int hw_irq) +{ + int irq = hw_irq - 0x20; + + if (irq < 0 || irq >= CONFIG_SYS_NUM_IRQS) { + printf("do_irq: bad irq number %d\n", irq); + return; + } + + if (irq_handlers[irq].handler) { + mask_irq(irq); + + irq_handlers[irq].handler(irq_handlers[irq].arg); + irq_handlers[irq].count++; + + unmask_irq(irq); + specific_eoi(irq); + + } else { + if ((irq & 7) != 7) { + spurious_irq_cnt++; + spurious_irq = irq; + } + } +} + +#if defined(CONFIG_CMD_IRQ) +int do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int irq; + + printf("Spurious IRQ: %u, last unknown IRQ: %d\n", + spurious_irq_cnt, spurious_irq); + + printf ("Interrupt-Information:\n"); + printf ("Nr Routine Arg Count\n"); + + for (irq = 0; irq <= CONFIG_SYS_NUM_IRQS; irq++) { + if (irq_handlers[irq].handler != NULL) { + printf ("%02d %08lx %08lx %d\n", + irq, + (ulong)irq_handlers[irq].handler, + (ulong)irq_handlers[irq].arg, + irq_handlers[irq].count); + } + } + + return 0; +} +#endif diff --git a/arch/x86/lib/pcat_interrupts.c b/arch/x86/lib/pcat_interrupts.c new file mode 100644 index 0000000000..364c435837 --- /dev/null +++ b/arch/x86/lib/pcat_interrupts.c @@ -0,0 +1,132 @@ +/* + * (C) Copyright 2009 + * Graeme Russ, <graeme.russ@gmail.com> + * + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +/* + * This file provides the interrupt handling functionality for systems + * based on the standard PC/AT architecture using two cascaded i8259 + * Programmable Interrupt Controllers. + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/i8259.h> +#include <asm/ibmpc.h> +#include <asm/interrupt.h> + +#if CONFIG_SYS_NUM_IRQS != 16 +#error "CONFIG_SYS_NUM_IRQS must equal 16 if CONFIG_SYS_NUM_IRQS is defined" +#endif + +int interrupt_init(void) +{ + u8 i; + + disable_interrupts(); + + /* Mask all interrupts */ + outb(0xff, MASTER_PIC + IMR); + outb(0xff, SLAVE_PIC + IMR); + + /* Master PIC */ + /* Place master PIC interrupts at INT20 */ + /* ICW3, One slave PIC is present */ + outb(ICW1_SEL|ICW1_EICW4, MASTER_PIC + ICW1); + outb(0x20, MASTER_PIC + ICW2); + outb(IR2, MASTER_PIC + ICW3); + outb(ICW4_PM, MASTER_PIC + ICW4); + + for (i = 0; i < 8; i++) + outb(OCW2_SEOI | i, MASTER_PIC + OCW2); + + /* Slave PIC */ + /* Place slave PIC interrupts at INT28 */ + /* Slave ID */ + outb(ICW1_SEL|ICW1_EICW4, SLAVE_PIC + ICW1); + outb(0x28, SLAVE_PIC + ICW2); + outb(0x02, SLAVE_PIC + ICW3); + outb(ICW4_PM, SLAVE_PIC + ICW4); + + for (i = 0; i < 8; i++) + outb(OCW2_SEOI | i, SLAVE_PIC + OCW2); + + /* + * Enable cascaded interrupts by unmasking the cascade IRQ pin of + * the master PIC + */ + unmask_irq (2); + + enable_interrupts(); + + return 0; +} + +void mask_irq(int irq) +{ + int imr_port; + + if (irq >= CONFIG_SYS_NUM_IRQS) + return; + + if (irq > 7) + imr_port = SLAVE_PIC + IMR; + else + imr_port = MASTER_PIC + IMR; + + outb(inb(imr_port) | (1 << (irq & 7)), imr_port); +} + +void unmask_irq(int irq) +{ + int imr_port; + + if (irq >= CONFIG_SYS_NUM_IRQS) + return; + + if (irq > 7) + imr_port = SLAVE_PIC + IMR; + else + imr_port = MASTER_PIC + IMR; + + outb(inb(imr_port) & ~(1 << (irq & 7)), imr_port); +} + +void specific_eoi(int irq) +{ + if (irq >= CONFIG_SYS_NUM_IRQS) + return; + + if (irq > 7) { + /* + * IRQ is on the slave - Issue a corresponding EOI to the + * slave PIC and an EOI for IRQ2 (the cascade interrupt) + * on the master PIC + */ + outb(OCW2_SEOI | (irq & 7), SLAVE_PIC + OCW2); + irq = SEOI_IR2; + } + + outb(OCW2_SEOI | irq, MASTER_PIC + OCW2); +} diff --git a/arch/x86/lib/pcat_timer.c b/arch/x86/lib/pcat_timer.c new file mode 100644 index 0000000000..1911c6c19d --- /dev/null +++ b/arch/x86/lib/pcat_timer.c @@ -0,0 +1,107 @@ +/* + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 <common.h> +#include <asm/io.h> +#include <asm/i8254.h> +#include <asm/ibmpc.h> +#include <asm/interrupt.h> + +#define TIMER0_VALUE 0x04aa /* 1kHz 1.9318MHz / 1000 */ +#define TIMER2_VALUE 0x0a8e /* 440Hz */ + +static int timer_init_done = 0; + +int timer_init(void) +{ + /* initialize timer 0 and 2 + * + * Timer 0 is used to increment system_tick 1000 times/sec + * Timer 1 was used for DRAM refresh in early PC's + * Timer 2 is used to drive the speaker + * (to stasrt a beep: write 3 to port 0x61, + * to stop it again: write 0) + */ + outb (PIT_CMD_CTR0 | PIT_CMD_BOTH | PIT_CMD_MODE2, + PIT_BASE + PIT_COMMAND); + outb (TIMER0_VALUE & 0xff, PIT_BASE + PIT_T0); + outb (TIMER0_VALUE >> 8, PIT_BASE + PIT_T0); + + outb (PIT_CMD_CTR2 | PIT_CMD_BOTH | PIT_CMD_MODE3, + PIT_BASE + PIT_COMMAND); + outb (TIMER2_VALUE & 0xff, PIT_BASE + PIT_T2); + outb (TIMER2_VALUE >> 8, PIT_BASE + PIT_T2); + + irq_install_handler (0, timer_isr, NULL); + unmask_irq (0); + + timer_init_done = 1; + + return 0; +} + +static u16 read_pit(void) +{ + u8 low; + + outb (PIT_CMD_LATCH, PIT_BASE + PIT_COMMAND); + low = inb (PIT_BASE + PIT_T0); + + return ((inb (PIT_BASE + PIT_T0) << 8) | low); +} + +/* this is not very exact */ +void __udelay (unsigned long usec) +{ + int counter; + int wraps; + + if (timer_init_done) + { + counter = read_pit (); + wraps = usec / 1000; + usec = usec % 1000; + + usec *= 1194; + usec /= 1000; + usec += counter; + + while (usec > 1194) { + usec -= 1194; + wraps++; + } + + while (1) { + int new_count = read_pit (); + + if (((new_count < usec) && !wraps) || wraps < 0) + break; + + if (new_count > counter) + wraps--; + + counter = new_count; + } + } + +} diff --git a/arch/x86/lib/pci.c b/arch/x86/lib/pci.c new file mode 100644 index 0000000000..e791e88bd4 --- /dev/null +++ b/arch/x86/lib/pci.c @@ -0,0 +1,150 @@ +/* + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 <common.h> +#include <pci.h> +#include <asm/io.h> +#include <asm/pci.h> + +#undef PCI_ROM_SCAN_VERBOSE + +int pci_shadow_rom(pci_dev_t dev, unsigned char *dest) +{ + struct pci_controller *hose; + int res = -1; + int i; + + u32 rom_addr; + u32 addr_reg; + u32 size; + + u16 vendor; + u16 device; + u32 class_code; + + hose = pci_bus_to_hose(PCI_BUS(dev)); +#if 0 + printf("pci_shadow_rom() asked to shadow device %x to %x\n", + dev, (u32)dest); +#endif + pci_read_config_word(dev, PCI_VENDOR_ID, &vendor); + pci_read_config_word(dev, PCI_DEVICE_ID, &device); + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_code); + + class_code &= 0xffffff00; + class_code >>= 8; + + debug("PCI Header Vendor %04x device %04x class %06x\n", + vendor, device, class_code); + + /* Enable the rom addess decoder */ + pci_write_config_dword(dev, PCI_ROM_ADDRESS, (u32)PCI_ROM_ADDRESS_MASK); + pci_read_config_dword(dev, PCI_ROM_ADDRESS, &addr_reg); + + if (!addr_reg) { + /* register unimplemented */ + printf("pci_chadow_rom: device do not seem to have a rom\n"); + return -1; + } + + size = (~(addr_reg&PCI_ROM_ADDRESS_MASK))+1; + + debug("ROM is %d bytes\n", size); + + rom_addr = pci_get_rom_window(hose, size); + + debug("ROM mapped at %x\n", rom_addr); + + pci_write_config_dword(dev, PCI_ROM_ADDRESS, + pci_phys_to_mem(dev, rom_addr) + |PCI_ROM_ADDRESS_ENABLE); + + + for (i=rom_addr;i<rom_addr+size; i+=512) { + + + if (readw(i) == 0xaa55) { + u32 pci_data; +#ifdef PCI_ROM_SCAN_VERBOSE + printf("ROM signature found\n"); +#endif + pci_data = readw(0x18+i); + pci_data += i; + + if (0==memcmp((void*)pci_data, "PCIR", 4)) { +#ifdef PCI_ROM_SCAN_VERBOSE + printf("Fount PCI rom image at offset %d\n", i-rom_addr); + printf("Vendor %04x device %04x class %06x\n", + readw(pci_data+4), readw(pci_data+6), + readl(pci_data+0x0d)&0xffffff); + printf("%s\n", + (readw(pci_data+0x15) &0x80)? + "Last image":"More images follow"); + switch (readb(pci_data+0x14)) { + case 0: + printf("X86 code\n"); + break; + case 1: + printf("Openfirmware code\n"); + break; + case 2: + printf("PARISC code\n"); + break; + } + printf("Image size %d\n", readw(pci_data+0x10) * 512); +#endif + /* FixMe: I think we should compare the class code + * bytes as well but I have no reference on the + * exact order of these bytes in the PCI ROM header */ + if (readw(pci_data+4) == vendor && + readw(pci_data+6) == device && + /* (readl(pci_data+0x0d)&0xffffff) == class_code && */ + readb(pci_data+0x14) == 0 /* x86 code image */ ) { +#ifdef PCI_ROM_SCAN_VERBOSE + printf("Suitable ROM image found, copying\n"); +#endif + memmove(dest, (void*)rom_addr, readw(pci_data+0x10) * 512); + res = 0; + break; + + } + if (readw(pci_data+0x15) &0x80) { + break; + } + } + } + + } + +#ifdef PCI_ROM_SCAN_VERBOSE + if (res) { + printf("No suitable image found\n"); + } +#endif + /* disable PAR register and PCI device ROM address devocer */ + pci_remove_rom_window(hose, rom_addr); + + pci_write_config_dword(dev, PCI_ROM_ADDRESS, 0); + + return res; +} diff --git a/arch/x86/lib/pci_type1.c b/arch/x86/lib/pci_type1.c new file mode 100644 index 0000000000..8ce5b33e3d --- /dev/null +++ b/arch/x86/lib/pci_type1.c @@ -0,0 +1,66 @@ +/* + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +/* + * Support for type PCI configuration cycles. + * based on pci_indirect.c + */ +#include <common.h> +#include <asm/io.h> +#include <pci.h> + +#define cfg_read(val, addr, op) *val = op((int)(addr)) +#define cfg_write(val, addr, op) op((val), (int)(addr)) + +#define TYPE1_PCI_OP(rw, size, type, op, mask) \ +static int \ +type1_##rw##_config_##size(struct pci_controller *hose, \ + pci_dev_t dev, int offset, type val) \ +{ \ + outl(dev | (offset & 0xfc) | 0x80000000, (int)hose->cfg_addr); \ + cfg_##rw(val, hose->cfg_data + (offset & mask), op); \ + return 0; \ +} + + +TYPE1_PCI_OP(read, byte, u8 *, inb, 3) +TYPE1_PCI_OP(read, word, u16 *, inw, 2) +TYPE1_PCI_OP(read, dword, u32 *, inl, 0) + +TYPE1_PCI_OP(write, byte, u8, outb, 3) +TYPE1_PCI_OP(write, word, u16, outw, 2) +TYPE1_PCI_OP(write, dword, u32, outl, 0) + +void pci_setup_type1(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data) +{ + pci_set_ops(hose, + type1_read_config_byte, + type1_read_config_word, + type1_read_config_dword, + type1_write_config_byte, + type1_write_config_word, + type1_write_config_dword); + + hose->cfg_addr = (unsigned int *) cfg_addr; + hose->cfg_data = (unsigned char *) cfg_data; +} diff --git a/arch/x86/lib/realmode.c b/arch/x86/lib/realmode.c new file mode 100644 index 0000000000..5be827c66b --- /dev/null +++ b/arch/x86/lib/realmode.c @@ -0,0 +1,96 @@ +/* + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 <common.h> +#include <asm/io.h> +#include <asm/ptrace.h> +#include <asm/realmode.h> + +#define REALMODE_MAILBOX ((char*)0xe00) + +extern ulong __realmode_start; +extern ulong __realmode_size; +extern char realmode_enter; + +int realmode_setup(void) +{ + ulong realmode_start = (ulong)&__realmode_start + gd->reloc_off; + ulong realmode_size = (ulong)&__realmode_size; + + /* copy the realmode switch code */ + if (realmode_size > (REALMODE_MAILBOX - (char *)REALMODE_BASE)) { + printf("realmode switch too large (%ld bytes, max is %d)\n", + realmode_size, + (REALMODE_MAILBOX - (char *)REALMODE_BASE)); + return -1; + } + + memcpy((char *)REALMODE_BASE, (void *)realmode_start, realmode_size); + asm("wbinvd\n"); + + return 0; +} + +int enter_realmode(u16 seg, u16 off, struct pt_regs *in, struct pt_regs *out) +{ + + /* setup out thin bios emulation */ + if (bios_setup()) + return -1; + + if (realmode_setup()) + return -1; + + in->eip = off; + in->xcs = seg; + if (3>(in->esp & 0xffff)) { + printf("Warning: entering realmode with sp < 4 will fail\n"); + } + + memcpy(REALMODE_MAILBOX, in, sizeof(struct pt_regs)); + asm("wbinvd\n"); + + __asm__ volatile ( + "lcall $0x20,%0\n" : : "i" (&realmode_enter) ); + + asm("wbinvd\n"); + memcpy(out, REALMODE_MAILBOX, sizeof(struct pt_regs)); + + return out->eax; +} + + +/* This code is supposed to access a realmode interrupt + * it does currently not work for me */ +int enter_realmode_int(u8 lvl, struct pt_regs *in, struct pt_regs *out) +{ + /* place two instructions at 0x700 */ + writeb(0xcd, 0x700); /* int $lvl */ + writeb(lvl, 0x701); + writeb(0xcb, 0x702); /* lret */ + asm("wbinvd\n"); + + enter_realmode(0x00, 0x700, in, out); + + return out->eflags&1; +} diff --git a/arch/x86/lib/realmode_switch.S b/arch/x86/lib/realmode_switch.S new file mode 100644 index 0000000000..fce4eccab0 --- /dev/null +++ b/arch/x86/lib/realmode_switch.S @@ -0,0 +1,221 @@ +/* + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, daniel@omicron.se + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +/* 32bit -> 16bit -> 32bit mode switch code */ + +/* + * Stack frame at 0xe00 + * e00 ebx; + * e04 ecx; + * e08 edx; + * e0c esi; + * e10 edi; + * e14 ebp; + * e18 eax; + * e1c ds; + * e20 es; + * e24 fs; + * e28 gs; + * e2c orig_eax; + * e30 eip; + * e34 cs; + * e38 eflags; + * e3c esp; + * e40 ss; + */ + +#define a32 .byte 0x67; /* address size prefix 32 */ +#define o32 .byte 0x66; /* operand size prefix 32 */ + +.section .realmode, "ax" +.code16 + /* 16bit protected mode code here */ +.globl realmode_enter +realmode_enter: +o32 pusha +o32 pushf + cli + sidt saved_idt + sgdt saved_gdt + movl %esp, %eax + movl %eax, saved_protected_mode_esp + + movl $0x10, %eax + movl %eax, %esp + movw $0x28, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + + lidt realmode_idt_ptr + movl %cr0, %eax /* Go back into real mode by */ + andl $0x7ffffffe, %eax /* clearing PE to 0 */ + movl %eax, %cr0 + ljmp $0x0,$do_realmode /* switch to real mode */ + +do_realmode: /* realmode code from here */ + movw %cs,%ax + movw %ax,%ds + movw %ax,%es + movw %ax,%fs + movw %ax,%gs + + /* create a temporary stack */ + + movw $0xc0, %ax + movw %ax, %ss + movw $0x200, %ax + movw %ax, %sp + + popl %ebx + popl %ecx + popl %edx + popl %esi + popl %edi + popl %ebp + popl %eax + movl %eax, temp_eax + popl %eax + movw %ax, %ds + popl %eax + movw %ax, %es + popl %eax + movw %ax, %fs + popl %eax + movw %ax, %gs + popl %eax /* orig_eax */ + popl %eax +cs movw %ax, temp_ip + popl %eax +cs movw %ax, temp_cs +o32 popf + popl %eax + popw %ss + movl %eax, %esp +cs movl temp_eax, %eax + wbinvd /* self-modifying code, + * better flush the cache */ + + .byte 0x9a /* lcall */ +temp_ip: + .word 0 /* new ip */ +temp_cs: + .word 0 /* new cs */ +realmode_ret: + /* save eax, esp and ss */ +cs movl %eax, saved_eax + movl %esp, %eax +cs movl %eax, saved_esp + movw %ss, %ax +cs movw %ax, saved_ss + + /* restore the stack, note that we set sp to 0x244; + * pt_regs is 0x44 bytes long and we push the structure + * backwards on to the stack, bottom first */ + + movw $0xc0, %ax + movw %ax, %ss + movw $0x244, %ax + movw %ax, %sp + + xorl %eax,%eax +cs movw saved_ss, %ax + pushl %eax +cs movl saved_esp, %eax + pushl %eax +o32 pushf + xorl %eax,%eax +cs movw temp_cs, %ax + pushl %eax +cs movw temp_ip, %ax + pushl %eax + pushl $0 + movw %gs, %ax + pushl %eax + movw %fs, %ax + pushl %eax + movw %es, %ax + pushl %eax + movw %ds, %ax + pushl %eax + movl saved_eax, %eax + pushl %eax + pushl %ebp + pushl %edi + pushl %esi + pushl %edx + pushl %ecx + pushl %ebx + +o32 cs lidt saved_idt +o32 cs lgdt saved_gdt /* Set GDTR */ + + movl %cr0, %eax /* Go back into protected mode */ + orl $1,%eax /* reset PE to 1 */ + movl %eax, %cr0 + jmp next_line /* flush prefetch queue */ +next_line: + movw $return_ptr, %ax + movw %ax,%bp +o32 cs ljmp *(%bp) + +.code32 +protected_mode: + movl $0x18,%eax /* reload GDT[3] */ + movw %ax,%fs /* reset FS */ + movw %ax,%ds /* reset DS */ + movw %ax,%gs /* reset GS */ + movw %ax,%es /* reset ES */ + movw %ax,%ss /* reset SS */ + movl saved_protected_mode_esp, %eax + movl %eax, %esp + popf + popa + ret + +temp_eax: + .long 0 + +saved_ss: + .word 0 +saved_esp: + .long 0 +saved_eax: + .long 0 + +realmode_idt_ptr: + .word 0x400 + .word 0x0, 0x0 + +saved_gdt: + .word 0, 0, 0, 0 +saved_idt: + .word 0, 0, 0, 0 + +saved_protected_mode_esp: + .long 0 + +return_ptr: + .long protected_mode + .word 0x10 diff --git a/arch/x86/lib/timer.c b/arch/x86/lib/timer.c new file mode 100644 index 0000000000..8fc68cdcb8 --- /dev/null +++ b/arch/x86/lib/timer.c @@ -0,0 +1,110 @@ +/* + * (C) Copyright 2008,2009 + * Graeme Russ, <graeme.russ@gmail.com> + * + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 <common.h> +#include <malloc.h> +#include <asm/io.h> +#include <asm/i8254.h> +#include <asm/ibmpc.h> + +struct timer_isr_function { + struct timer_isr_function *next; + timer_fnc_t *isr_func; +}; + +static struct timer_isr_function *first_timer_isr = NULL; +static volatile unsigned long system_ticks = 0; + +/* + * register_timer_isr() allows multiple architecture and board specific + * functions to be called every millisecond. Keep the execution time of + * each function as low as possible + */ +int register_timer_isr (timer_fnc_t *isr_func) +{ + struct timer_isr_function *new_func; + struct timer_isr_function *temp; + int flag; + + new_func = malloc(sizeof(struct timer_isr_function)); + + if (new_func == NULL) + return 1; + + new_func->isr_func = isr_func; + new_func->next = NULL; + + /* + * Don't allow timer interrupts while the + * linked list is being modified + */ + flag = disable_interrupts (); + + if (first_timer_isr == NULL) { + first_timer_isr = new_func; + } else { + temp = first_timer_isr; + while (temp->next != NULL) + temp = temp->next; + temp->next = new_func; + } + + if (flag) + enable_interrupts (); + + return 0; +} + +/* + * timer_isr() MUST be the registered interrupt handler for + */ +void timer_isr(void *unused) +{ + struct timer_isr_function *temp = first_timer_isr; + + system_ticks++; + + /* Execute each registered function */ + while (temp != NULL) { + temp->isr_func (); + temp = temp->next; + } +} + +void reset_timer (void) +{ + system_ticks = 0; +} + +ulong get_timer (ulong base) +{ + return (system_ticks - base); +} + +void set_timer (ulong t) +{ + system_ticks = t; +} diff --git a/arch/x86/lib/video.c b/arch/x86/lib/video.c new file mode 100644 index 0000000000..b29075c490 --- /dev/null +++ b/arch/x86/lib/video.c @@ -0,0 +1,229 @@ +/* + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 <common.h> +#include <pci.h> +#include <stdio_dev.h> +#include <i8042.h> +#include <asm/ptrace.h> +#include <asm/realmode.h> +#include <asm/io.h> +#include <asm/pci.h> + +/* basic textmode I/O from linux kernel */ +static char *vidmem = (char *)0xb8000; +static int vidport; +static int lines, cols; +static int orig_x, orig_y; + +static void beep(int dur) +{ + int i; + + outb_p(3, 0x61); + for (i = 0; i < 10*dur; i++) + udelay(1000); + + outb_p(0, 0x61); +} + +static void scroll(void) +{ + int i; + + memcpy(vidmem, vidmem + cols * 2, (lines - 1) * cols * 2); + for (i = (lines - 1) * cols * 2; i < lines * cols * 2; i += 2) + vidmem[i] = ' '; +} + +static void __video_putc(const char c, int *x, int *y) +{ + if (c == '\n') { + (*x) = 0; + if (++(*y) >= lines) { + scroll(); + (*y)--; + } + } else if (c == '\b') { + if ((*x) != 0) { + --(*x); + vidmem[((*x) + cols * (*y)) * 2] = ' '; + } + } else if (c == '\r') { + (*x) = 0; + + } else if (c == '\a') { + beep(3); + + } else if (c == '\t') { + __video_putc(' ', x, y); + __video_putc(' ', x, y); + __video_putc(' ', x, y); + __video_putc(' ', x, y); + __video_putc(' ', x, y); + __video_putc(' ', x, y); + __video_putc(' ', x, y); + __video_putc(' ', x, y); + } else if (c == '\v') { + switch ((*x) % 8) { + case 0: + __video_putc(' ', x, y); + case 7: + __video_putc(' ', x, y); + case 6: + __video_putc(' ', x, y); + case 5: + __video_putc(' ', x, y); + case 4: + __video_putc(' ', x, y); + case 3: + __video_putc(' ', x, y); + case 2: + __video_putc(' ', x, y); + case 1: + __video_putc(' ', x, y); + } + } else if (c == '\f') { + int i; + for (i = 0; i < lines * cols * 2; i += 2) + vidmem[i] = 0; + (*x) = 0; + (*y) = 0; + } else { + vidmem[((*x) + cols * (*y)) * 2] = c; + if (++(*x) >= cols) { + (*x) = 0; + if (++(*y) >= lines) { + scroll(); + (*y)--; + } + } + } +} + +static void video_putc(const char c) +{ + int x,y,pos; + + x = orig_x; + y = orig_y; + + __video_putc(c, &x, &y); + + orig_x = x; + orig_y = y; + + pos = (x + cols * y) * 2; /* Update cursor position */ + outb_p(14, vidport); + outb_p(0xff & (pos >> 9), vidport+1); + outb_p(15, vidport); + outb_p(0xff & (pos >> 1), vidport+1); +} + +static void video_puts(const char *s) +{ + int x,y,pos; + char c; + + x = orig_x; + y = orig_y; + + while ((c = *s++) != '\0') + __video_putc(c, &x, &y); + + orig_x = x; + orig_y = y; + + pos = (x + cols * y) * 2; /* Update cursor position */ + outb_p(14, vidport); + outb_p(0xff & (pos >> 9), vidport+1); + outb_p(15, vidport); + outb_p(0xff & (pos >> 1), vidport+1); +} + +int video_init(void) +{ + u16 pos; + + static struct stdio_dev vga_dev; + static struct stdio_dev kbd_dev; + + vidmem = (char *) 0xb8000; + vidport = 0x3d4; + + lines = 25; + cols = 80; + + outb_p(14, vidport); + pos = inb_p(vidport+1); + pos <<= 8; + outb_p(15, vidport); + pos |= inb_p(vidport+1); + + orig_x = pos%cols; + orig_y = pos/cols; + +#if 0 + printf("pos %x %d %d\n", pos, orig_x, orig_y); +#endif + if (orig_y > lines) + orig_x = orig_y =0; + + memset(&vga_dev, 0, sizeof(vga_dev)); + strcpy(vga_dev.name, "vga"); + vga_dev.ext = 0; + vga_dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_SYSTEM; + vga_dev.putc = video_putc; /* 'putc' function */ + vga_dev.puts = video_puts; /* 'puts' function */ + vga_dev.tstc = NULL; /* 'tstc' function */ + vga_dev.getc = NULL; /* 'getc' function */ + + if (stdio_register(&vga_dev) == 0) + return 1; + + if (i8042_kbd_init()) + return 1; + + memset(&kbd_dev, 0, sizeof(kbd_dev)); + strcpy(kbd_dev.name, "kbd"); + kbd_dev.ext = 0; + kbd_dev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; + kbd_dev.putc = NULL; /* 'putc' function */ + kbd_dev.puts = NULL; /* 'puts' function */ + kbd_dev.tstc = i8042_tstc; /* 'tstc' function */ + kbd_dev.getc = i8042_getc; /* 'getc' function */ + + if (stdio_register(&kbd_dev) == 0) + return 1; + + return 0; +} + + +int drv_video_init(void) +{ + if (video_bios_init()) + return 1; + + return video_init(); +} diff --git a/arch/x86/lib/video_bios.c b/arch/x86/lib/video_bios.c new file mode 100644 index 0000000000..6bc4335743 --- /dev/null +++ b/arch/x86/lib/video_bios.c @@ -0,0 +1,222 @@ +/* + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 <common.h> +#include <pci.h> +#include <malloc.h> +#include <asm/ptrace.h> +#include <asm/realmode.h> +#include <asm/io.h> +#include <asm/pci.h> + +#undef PCI_BIOS_DEBUG +#undef VGA_BIOS_DEBUG + +#ifdef VGA_BIOS_DEBUG +#define PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define PRINTF(fmt,args...) +#endif + +#ifdef CONFIG_PCI + +#ifdef PCI_BIOS_DEBUG +#define RELOC_16(seg, off) *(u32*)(seg << 4 | (u32)&off) +extern u32 num_pci_bios_present; +extern u32 num_pci_bios_find_device; +extern u32 num_pci_bios_find_class; +extern u32 num_pci_bios_generate_special_cycle; +extern u32 num_pci_bios_read_cfg_byte; +extern u32 num_pci_bios_read_cfg_word; +extern u32 num_pci_bios_read_cfg_dword; +extern u32 num_pci_bios_write_cfg_byte; +extern u32 num_pci_bios_write_cfg_word; +extern u32 num_pci_bios_write_cfg_dword; +extern u32 num_pci_bios_get_irq_routing; +extern u32 num_pci_bios_set_irq; +extern u32 num_pci_bios_unknown_function; + +void print_bios_bios_stat(void) +{ + printf("16 bit functions:\n"); + printf("pci_bios_present: %d\n", RELOC_16(0xf000, num_pci_bios_present)); + printf("pci_bios_find_device: %d\n", RELOC_16(0xf000, num_pci_bios_find_device)); + printf("pci_bios_find_class: %d\n", RELOC_16(0xf000, num_pci_bios_find_class)); + printf("pci_bios_generate_special_cycle: %d\n", RELOC_16(0xf000, num_pci_bios_generate_special_cycle)); + printf("pci_bios_read_cfg_byte: %d\n", RELOC_16(0xf000, num_pci_bios_read_cfg_byte)); + printf("pci_bios_read_cfg_word: %d\n", RELOC_16(0xf000, num_pci_bios_read_cfg_word)); + printf("pci_bios_read_cfg_dword: %d\n", RELOC_16(0xf000, num_pci_bios_read_cfg_dword)); + printf("pci_bios_write_cfg_byte: %d\n", RELOC_16(0xf000, num_pci_bios_write_cfg_byte)); + printf("pci_bios_write_cfg_word: %d\n", RELOC_16(0xf000, num_pci_bios_write_cfg_word)); + printf("pci_bios_write_cfg_dword: %d\n", RELOC_16(0xf000, num_pci_bios_write_cfg_dword)); + printf("pci_bios_get_irq_routing: %d\n", RELOC_16(0xf000, num_pci_bios_get_irq_routing)); + printf("pci_bios_set_irq: %d\n", RELOC_16(0xf000, num_pci_bios_set_irq)); + printf("pci_bios_unknown_function: %d\n", RELOC_16(0xf000, num_pci_bios_unknown_function)); + +} +#endif + +#ifdef CONFIG_VIDEO + +#define PCI_CLASS_VIDEO 3 +#define PCI_CLASS_VIDEO_STD 0 +#define PCI_CLASS_VIDEO_PROG_IF_VGA 0 + +static struct pci_device_id supported[] = { + {PCI_VIDEO_VENDOR_ID, PCI_VIDEO_DEVICE_ID}, + {} +}; + +static u32 probe_pci_video(void) +{ + pci_dev_t devbusfn; + + if ((devbusfn = pci_find_devices(supported, 0) != -1)) { + u32 old; + u32 addr; + + /* PCI video device detected */ + printf("Found PCI VGA device at %02x.%02x.%x\n", + PCI_BUS(devbusfn), PCI_DEV(devbusfn), PCI_FUNC(devbusfn)); + + /* Enable I/O decoding as well, PCI viudeo boards + * support I/O accesses, but they provide no + * bar register for this since the ports are fixed. + */ + pci_write_config_word(devbusfn, PCI_COMMAND, PCI_COMMAND_MEMORY | PCI_COMMAND_IO | PCI_COMMAND_MASTER); + + /* Test the ROM decoder, do the device support a rom? */ + pci_read_config_dword(devbusfn, PCI_ROM_ADDRESS, &old); + pci_write_config_dword(devbusfn, PCI_ROM_ADDRESS, (u32)PCI_ROM_ADDRESS_MASK); + pci_read_config_dword(devbusfn, PCI_ROM_ADDRESS, &addr); + pci_write_config_dword(devbusfn, PCI_ROM_ADDRESS, old); + + if (!addr) { + printf("PCI VGA have no ROM?\n"); + return 0; + } + + /* device have a rom */ + if (pci_shadow_rom(devbusfn, (void*)0xc0000)) { + printf("Shadowing of PCI VGA BIOS failed\n"); + return 0; + } + + /* Now enable lagacy VGA port access */ + if (pci_enable_legacy_video_ports(pci_bus_to_hose(PCI_BUS(devbusfn)))) { + printf("PCI VGA enable failed\n"); + return 0; + } + + + /* return the pci device info, that we'll need later */ + return PCI_BUS(devbusfn) << 8 | + PCI_DEV(devbusfn) << 3 | (PCI_FUNC(devbusfn)&7); + } + + return 0; +} + +static int probe_isa_video(void) +{ + u32 ptr; + char *buf; + + if (0 == (ptr = isa_map_rom(0xc0000, 0x8000))) { + return -1; + } + if (NULL == (buf=malloc(0x8000))) { + isa_unmap_rom(ptr); + return -1; + } + if (readw(ptr) != 0xaa55) { + free(buf); + isa_unmap_rom(ptr); + return -1; + } + + /* shadow the rom */ + memcpy(buf, (void*)ptr, 0x8000); + isa_unmap_rom(ptr); + memcpy((void*)0xc0000, buf, 0x8000); + + free(buf); + + return 0; +} + +int video_bios_init(void) +{ + struct pt_regs regs; + + /* clear the video bios area in case we warmbooted */ + memset((void*)0xc0000, 0, 0x8000); + memset(®s, 0, sizeof(struct pt_regs)); + + if (probe_isa_video()) { + /* No ISA board found, try the PCI bus */ + regs.eax = probe_pci_video(); + } + + /* Did we succeed in mapping any video bios */ + if (readw(0xc0000) == 0xaa55) { + int size; + int i; + u8 sum; + + PRINTF("Found video bios signature\n"); + size = 512*readb(0xc0002); + PRINTF("size %d\n", size); + sum=0; + for (i=0;i<size;i++) { + sum += readb(0xc0000 + i); + } + PRINTF("Checksum is %sOK\n",sum?"NOT ":""); + if (sum) { + return 1; + } + + /* some video bioses (ATI Mach64) seem to think that + * the original int 10 handler is always at + * 0xf000:0xf065 , place an iret instruction there + */ + writeb(0xcf, 0xff065); + + regs.esp = 0x8000; + regs.xss = 0x2000; + enter_realmode(0xc000, 3, ®s, ®s); + PRINTF("INT 0x10 vector after: %04x:%04x\n", + readw(0x42), readw(0x40)); + PRINTF("BIOS returned %scarry\n", regs.eflags & 1?"":"NOT "); +#ifdef PCI_BIOS_DEBUG + print_bios_bios_stat(); +#endif + return (regs.eflags & 1); + + } + + return 1; + +} +#endif +#endif diff --git a/arch/x86/lib/zimage.c b/arch/x86/lib/zimage.c new file mode 100644 index 0000000000..cc4b40e64c --- /dev/null +++ b/arch/x86/lib/zimage.c @@ -0,0 +1,296 @@ +/* + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +/* + * Linux x86 zImage and bzImage loading + * + * based on the procdure described in + * linux/Documentation/i386/boot.txt + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/ptrace.h> +#include <asm/zimage.h> +#include <asm/realmode.h> +#include <asm/byteorder.h> +#include <asm/bootparam.h> +#include <asm/ic/sc520.h> + +/* + * Memory lay-out: + * + * relative to setup_base (which is 0x90000 currently) + * + * 0x0000-0x7FFF Real mode kernel + * 0x8000-0x8FFF Stack and heap + * 0x9000-0x90FF Kernel command line + */ +#define DEFAULT_SETUP_BASE 0x90000 +#define COMMAND_LINE_OFFSET 0x9000 +#define HEAP_END_OFFSET 0x8e00 + +#define COMMAND_LINE_SIZE 2048 + +static void build_command_line(char *command_line, int auto_boot) +{ + char *env_command_line; + + command_line[0] = '\0'; + + env_command_line = getenv("bootargs"); + + /* set console= argument if we use a serial console */ + if (NULL == strstr(env_command_line, "console=")) { + if (0==strcmp(getenv("stdout"), "serial")) { + + /* We seem to use serial console */ + sprintf(command_line, "console=ttyS0,%s ", + getenv("baudrate")); + } + } + + if (auto_boot) { + strcat(command_line, "auto "); + } + + if (NULL != env_command_line) { + strcat(command_line, env_command_line); + } + + + printf("Kernel command line: \"%s\"\n", command_line); +} + +void *load_zimage(char *image, unsigned long kernel_size, + unsigned long initrd_addr, unsigned long initrd_size, + int auto_boot) +{ + void *setup_base; + int setup_size; + int bootproto; + int big_image; + void *load_address; + + struct setup_header *hdr = (struct setup_header *)(image + SETUP_SECTS_OFF); + + setup_base = (void*)DEFAULT_SETUP_BASE; /* base address for real-mode segment */ + + if (KERNEL_MAGIC != hdr->boot_flag) { + printf("Error: Invalid Boot Flag (found 0x%04x, expected 0x%04x)\n", + hdr->boot_flag, KERNEL_MAGIC); + return 0; + } else { + printf("Valid Boot Flag\n"); + } + + /* determine boot protocol version */ + if (KERNEL_V2_MAGIC == hdr->header) { + printf("Magic signature found\n"); + + bootproto = hdr->version; + } else { + /* Very old kernel */ + printf("Magic signature not found\n"); + bootproto = 0x0100; + } + + /* determine size of setup */ + if (0 == hdr->setup_sects) { + printf("Setup Sectors = 0 (defaulting to 4)\n"); + setup_size = 5 * 512; + } else { + setup_size = (hdr->setup_sects + 1) * 512; + } + + printf("Setup Size = 0x%8.8lx\n", (ulong)setup_size); + + if (setup_size > SETUP_MAX_SIZE) { + printf("Error: Setup is too large (%d bytes)\n", setup_size); + } + + /* Determine image type */ + big_image = (bootproto >= 0x0200) && (hdr->loadflags & BIG_KERNEL_FLAG); + + /* Determine load address */ + load_address = (void*)(big_image ? BZIMAGE_LOAD_ADDR : ZIMAGE_LOAD_ADDR); + + /* load setup */ + printf("Moving Real-Mode Code to 0x%8.8lx (%d bytes)\n", (ulong)setup_base, setup_size); + memmove(setup_base, image, setup_size); + + printf("Using boot protocol version %x.%02x\n", + (bootproto & 0xff00) >> 8, bootproto & 0xff); + + if (bootproto == 0x0100) { + + *(u16*)(setup_base + CMD_LINE_MAGIC_OFF) = COMMAND_LINE_MAGIC; + *(u16*)(setup_base + CMD_LINE_OFFSET_OFF) = COMMAND_LINE_OFFSET; + + /* A very old kernel MUST have its real-mode code + * loaded at 0x90000 */ + + if ((u32)setup_base != 0x90000) { + /* Copy the real-mode kernel */ + memmove((void*)0x90000, setup_base, setup_size); + /* Copy the command line */ + memmove((void*)0x99000, setup_base+COMMAND_LINE_OFFSET, + COMMAND_LINE_SIZE); + + setup_base = (void*)0x90000; /* Relocated */ + } + + /* It is recommended to clear memory up to the 32K mark */ + memset((void*)0x90000 + setup_size, 0, SETUP_MAX_SIZE-setup_size); + } + + /* We are now setting up the real-mode version of the header */ + hdr = (struct setup_header *)(setup_base + SETUP_SECTS_OFF); + + if (bootproto >= 0x0200) { + hdr->type_of_loader = 8; + + if (hdr->setup_sects >= 15) + printf("Linux kernel version %s\n", (char *) + (setup_base + (hdr->kernel_version + 0x200))); + else + printf("Setup Sectors < 15 - Cannot print kernel version.\n"); + + if (initrd_addr) { + printf("Initial RAM disk at linear address 0x%08lx, size %ld bytes\n", + initrd_addr, initrd_size); + + hdr->ramdisk_image = initrd_addr; + hdr->ramdisk_size = initrd_size; + } + } + + if (bootproto >= 0x0201) { + hdr->heap_end_ptr = HEAP_END_OFFSET; + hdr->loadflags |= HEAP_FLAG; + } + + if (bootproto >= 0x0202) { + hdr->cmd_line_ptr = (u32)setup_base + COMMAND_LINE_OFFSET; + } else if (bootproto >= 0x0200) { + + *(u16*)(setup_base + CMD_LINE_MAGIC_OFF) = COMMAND_LINE_MAGIC; + *(u16*)(setup_base + CMD_LINE_OFFSET_OFF) = COMMAND_LINE_OFFSET; + + hdr->setup_move_size = 0x9100; + } + + if (bootproto >= 0x0204) + kernel_size = hdr->syssize * 16; + else + kernel_size -= setup_size; + + + if (big_image) { + if ((kernel_size) > BZIMAGE_MAX_SIZE) { + printf("Error: bzImage kernel too big! (size: %ld, max: %d)\n", + kernel_size, BZIMAGE_MAX_SIZE); + return 0; + } + + } else if ((kernel_size) > ZIMAGE_MAX_SIZE) { + printf("Error: zImage kernel too big! (size: %ld, max: %d)\n", + kernel_size, ZIMAGE_MAX_SIZE); + return 0; + } + + /* build command line at COMMAND_LINE_OFFSET */ + build_command_line(setup_base + COMMAND_LINE_OFFSET, auto_boot); + + printf("Loading %czImage at address 0x%08x (%ld bytes)\n", big_image ? 'b' : ' ', + (u32)load_address, kernel_size); + + + memmove(load_address, image + setup_size, kernel_size); + + /* ready for booting */ + return setup_base; +} + +void boot_zimage(void *setup_base) +{ + struct pt_regs regs; + + memset(®s, 0, sizeof(struct pt_regs)); + regs.xds = (u32)setup_base >> 4; + regs.xes = regs.xds; + regs.xss = regs.xds; + regs.esp = 0x9000; + regs.eflags = 0; + enter_realmode(((u32)setup_base+SETUP_START_OFFSET)>>4, 0, ®s, ®s); +} + +int do_zboot (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + void *base_ptr; + void *bzImage_addr = NULL; + char *s; + ulong bzImage_size = 0; + + disable_interrupts(); + + /* Setup board for maximum PC/AT Compatibility */ + setup_pcat_compatibility(); + + if (argc >= 2) + /* argv[1] holds the address of the bzImage */ + s = argv[1]; + else + s = getenv("fileaddr"); + + if (s) + bzImage_addr = (void *)simple_strtoul(s, NULL, 16); + + if (argc >= 3) + /* argv[2] holds the size of the bzImage */ + bzImage_size = simple_strtoul(argv[2], NULL, 16); + + /* Lets look for*/ + base_ptr = load_zimage (bzImage_addr, bzImage_size, 0, 0, 0); + + if (NULL == base_ptr) { + printf ("## Kernel loading failed ...\n"); + } else { + printf ("## Transferring control to Linux (at address %08x) ...\n", + (u32)base_ptr); + + /* we assume that the kernel is in place */ + printf("\nStarting kernel ...\n\n"); + + boot_zimage(base_ptr); + /* does not return */ + } + + return -1; +} + +U_BOOT_CMD( + zboot, 2, 0, do_zboot, + "Boot bzImage", + "" +); |