diff options
Diffstat (limited to 'arch/i386/lib')
| -rw-r--r-- | arch/i386/lib/Makefile | 59 | ||||
| -rw-r--r-- | arch/i386/lib/bios.S | 532 | ||||
| -rw-r--r-- | arch/i386/lib/bios.h | 94 | ||||
| -rw-r--r-- | arch/i386/lib/bios_pci.S | 413 | ||||
| -rw-r--r-- | arch/i386/lib/bios_setup.c | 242 | ||||
| -rw-r--r-- | arch/i386/lib/board.c | 429 | ||||
| -rw-r--r-- | arch/i386/lib/bootm.c | 94 | ||||
| -rw-r--r-- | arch/i386/lib/interrupts.c | 161 | ||||
| -rw-r--r-- | arch/i386/lib/pcat_interrupts.c | 132 | ||||
| -rw-r--r-- | arch/i386/lib/pcat_timer.c | 107 | ||||
| -rw-r--r-- | arch/i386/lib/pci.c | 152 | ||||
| -rw-r--r-- | arch/i386/lib/pci_type1.c | 51 | ||||
| -rw-r--r-- | arch/i386/lib/realmode.c | 100 | ||||
| -rw-r--r-- | arch/i386/lib/realmode_switch.S | 222 | ||||
| -rw-r--r-- | arch/i386/lib/timer.c | 107 | ||||
| -rw-r--r-- | arch/i386/lib/video.c | 237 | ||||
| -rw-r--r-- | arch/i386/lib/video_bios.c | 222 | ||||
| -rw-r--r-- | arch/i386/lib/zimage.c | 225 | 
18 files changed, 3579 insertions, 0 deletions
| diff --git a/arch/i386/lib/Makefile b/arch/i386/lib/Makefile new file mode 100644 index 000000000..983850668 --- /dev/null +++ b/arch/i386/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).a + +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) +	$(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/arch/i386/lib/bios.S b/arch/i386/lib/bios.S new file mode 100644 index 000000000..48f1b8112 --- /dev/null +++ b/arch/i386/lib/bios.S @@ -0,0 +1,532 @@ +/* + * (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/i386/lib/bios.h b/arch/i386/lib/bios.h new file mode 100644 index 000000000..4901f8917 --- /dev/null +++ b/arch/i386/lib/bios.h @@ -0,0 +1,94 @@ +/* + * (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/i386/lib/bios_pci.S b/arch/i386/lib/bios_pci.S new file mode 100644 index 000000000..9e412e5e4 --- /dev/null +++ b/arch/i386/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/i386/lib/bios_setup.c b/arch/i386/lib/bios_setup.c new file mode 100644 index 000000000..6491e522e --- /dev/null +++ b/arch/i386/lib/bios_setup.c @@ -0,0 +1,242 @@ +/* + * (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 _i386boot_bios; +extern ulong _i386boot_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 i386boot_bios      = (ulong)&_i386boot_bios; +	ulong i386boot_bios_size = (ulong)&_i386boot_bios_size; + +	static int done=0; +	int vector; +#ifdef CONFIG_PCI +	struct pci_controller *pri_hose; +#endif +	if (done) { +		return 0; +	} +	done = 1; + +	if (i386boot_bios_size > 65536) { +		printf("BIOS too large (%ld bytes, max is 65536)\n", +		       i386boot_bios_size); +		return -1; +	} + +	memcpy(BIOS_BASE, (void*)i386boot_bios, i386boot_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/i386/lib/board.c b/arch/i386/lib/board.c new file mode 100644 index 000000000..f3b634855 --- /dev/null +++ b/arch/i386/lib/board.c @@ -0,0 +1,429 @@ +/* + * (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 <asm/u-boot-i386.h> +#include <elf.h> + +#ifdef CONFIG_BITBANGMII +#include <miiphy.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +/* Exports from the Linker Script */ +extern ulong _i386boot_text_start; +extern ulong _i386boot_rel_dyn_start; +extern ulong _i386boot_rel_dyn_end; +extern ulong _i386boot_bss_start; +extern ulong _i386boot_bss_size; +void ram_bootstrap (void *); +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_r("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); +} + +/* + * WARNING: this code looks "cleaner" than the PowerPC version, but + * has the disadvantage that you either get nothing, or everything. + * On PowerPC, you might see "DRAM: " before the system hangs - which + * gives a simple yet clear indication which part of the + * initialization if failing. + */ +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); + +init_fnc_t *init_sequence[] = { +	serial_init, +	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, +	env_init,		/* initialize environment */ +	init_baudrate,		/* initialze baudrate settings */ +	serial_init,		/* serial communications setup */ +	display_banner, +	display_dram_config, + +	NULL, +}; + +gd_t *gd; + +/* + * Load U-Boot into RAM, initialize BSS, perform relocation adjustments + */ +void board_init_f (ulong stack_limit) +{ +	void *text_start = &_i386boot_text_start; +	void *u_boot_cmd_end = &__u_boot_cmd_end; +	Elf32_Rel *rel_dyn_start = (Elf32_Rel *)&_i386boot_rel_dyn_start; +	Elf32_Rel *rel_dyn_end = (Elf32_Rel *)&_i386boot_rel_dyn_end; +	void *bss_start = &_i386boot_bss_start; +	void *bss_size = &_i386boot_bss_size; + +	size_t uboot_size; +	void *ram_start; +	ulong rel_offset; +	Elf32_Rel *re; + +	void (*start_func)(void *); + +	/* compiler optimization barrier needed for GCC >= 3.4 */ +	__asm__ __volatile__("": : :"memory"); + +	uboot_size = (size_t)u_boot_cmd_end - (size_t)text_start; +	ram_start  = (void *)stack_limit - (uboot_size + (ulong)bss_size); +	rel_offset = text_start - ram_start; +	start_func = ram_bootstrap - rel_offset; + +	/* First stage CPU initialization */ +	if (cpu_init_f() != 0) +		hang(); + +	/* First stage Board initialization */ +	if (board_early_init_f() != 0) +		hang(); + +	/* Copy U-Boot into RAM */ +	memcpy(ram_start, text_start, (size_t)uboot_size); + +	/* Clear BSS */ +	memset(bss_start - rel_offset,	0, (size_t)bss_size); + +	/* Perform relocation adjustments */ +	for (re = rel_dyn_start; re < rel_dyn_end; re++) +	{ +		if (re->r_offset >= TEXT_BASE) +			if (*(ulong *)re->r_offset >= TEXT_BASE) +				*(ulong *)(re->r_offset - rel_offset) -= (Elf32_Addr)rel_offset; +	} + +	start_func(ram_start); + +	/* NOTREACHED - relocate_code() does not return */ +	while(1); +} + +/* + * All attempts to jump straight from board_init_f() to board_init_r() + * have failed, hence this special 'bootstrap' function. + */ +void ram_bootstrap (void *ram_start) +{ +	static gd_t gd_data; + +	/* compiler optimization barrier needed for GCC >= 3.4 */ +	__asm__ __volatile__("": : :"memory"); + +	board_init_r(&gd_data, (ulong)ram_start); +} + +void board_init_r(gd_t *id, ulong ram_start) +{ +	char *s; +	int i; +	ulong size; +	static bd_t bd_data; +	init_fnc_t **init_fnc_ptr; + +	show_boot_progress(0x21); + +	gd = id; +	/* compiler optimization barrier needed for GCC >= 3.4 */ +	__asm__ __volatile__("": : :"memory"); + +	memset (gd, 0, sizeof (gd_t)); +	gd->bd = &bd_data; +	memset (gd->bd, 0, sizeof (bd_t)); +	show_boot_progress(0x22); + +	gd->baudrate =  CONFIG_BAUDRATE; + +	gd->flags |= GD_FLG_RELOC;	/* tell others: relocation done */ + +	mem_malloc_init((((ulong)ram_start - CONFIG_SYS_MALLOC_LEN)+3)&~3, +			CONFIG_SYS_MALLOC_LEN); + +	for (init_fnc_ptr = init_sequence, i=0; *init_fnc_ptr; ++init_fnc_ptr, i++) { +		show_boot_progress(0xa130|i); + +		if ((*init_fnc_ptr)() != 0) { +			hang (); +		} +	} +	show_boot_progress(0x23); + +	/* 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); + + +	/* IP Address */ +	bd_data.bi_ip_addr = getenv_IPaddr ("ipaddr"); + +#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); + +	/* Must happen after interrupts are initialized since +	 * an irq handler gets installed +	 */ +#ifdef CONFIG_SERIAL_SOFTWARE_FIFO +	serial_buffered_init(); +#endif + +#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 *[]), int argc, char *argv[]) +{ +	/* +	 * TODO: Test this function - changed to fix compiler error. +	 * Original code was: +	 *   return (entry >> 1) (argc, argv); +	 * with a comment about Nios function pointers are address >> 1 +	 */ +	return (entry) (argc, argv); +} diff --git a/arch/i386/lib/bootm.c b/arch/i386/lib/bootm.c new file mode 100644 index 000000000..f96d7bd6d --- /dev/null +++ b/arch/i386/lib/bootm.c @@ -0,0 +1,94 @@ +/* + * (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) + * + * 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 *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/i386/lib/interrupts.c b/arch/i386/lib/interrupts.c new file mode 100644 index 000000000..51def5995 --- /dev/null +++ b/arch/i386/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 <huber@mclx.com>, Mission Critical Linux, Inc. + * + * 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 i386 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 i386 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 *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/i386/lib/pcat_interrupts.c b/arch/i386/lib/pcat_interrupts.c new file mode 100644 index 000000000..67e6e97e3 --- /dev/null +++ b/arch/i386/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/i386/lib/pcat_timer.c b/arch/i386/lib/pcat_timer.c new file mode 100644 index 000000000..1373fd125 --- /dev/null +++ b/arch/i386/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/i386/lib/pci.c b/arch/i386/lib/pci.c new file mode 100644 index 000000000..9020e7ce7 --- /dev/null +++ b/arch/i386/lib/pci.c @@ -0,0 +1,152 @@ +/* + * (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; + +#if 0 +	printf("PCI Header Vendor %04x device %04x class %06x\n", +	       vendor, device, class_code); +#endif +	/* 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; + +#if 0 +	printf("ROM is %d bytes\n", size); +#endif +	rom_addr = pci_get_rom_window(hose, size); +#if 0 +	printf("ROM mapped at %x \n", rom_addr); +#endif +	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/i386/lib/pci_type1.c b/arch/i386/lib/pci_type1.c new file mode 100644 index 000000000..225ae4a99 --- /dev/null +++ b/arch/i386/lib/pci_type1.c @@ -0,0 +1,51 @@ +/* + * Support for type PCI configuration cycles. + * based on pci_indirect.c + * + * Copyright (C) 2002 Daniel Engström, Omicron Ceti AB, daniel@omicron.se. + * + * 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. + */ + +#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/i386/lib/realmode.c b/arch/i386/lib/realmode.c new file mode 100644 index 000000000..3c3c1fc96 --- /dev/null +++ b/arch/i386/lib/realmode.c @@ -0,0 +1,100 @@ +/* + * (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_BASE    ((char*)0x7c0) +#define REALMODE_MAILBOX ((char*)0xe00) + + +extern ulong _i386boot_realmode; +extern ulong _i386boot_realmode_size; +extern char realmode_enter; + +int realmode_setup(void) +{ +	ulong i386boot_realmode      = (ulong)&_i386boot_realmode; +	ulong i386boot_realmode_size = (ulong)&_i386boot_realmode_size; + +	/* copy the realmode switch code */ +	if (i386boot_realmode_size > (REALMODE_MAILBOX-REALMODE_BASE)) { +		printf("realmode switch too large (%ld bytes, max is %d)\n", +		       i386boot_realmode_size, (REALMODE_MAILBOX-REALMODE_BASE)); +		return -1; +	} + +	memcpy(REALMODE_BASE, (void*)i386boot_realmode, i386boot_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/i386/lib/realmode_switch.S b/arch/i386/lib/realmode_switch.S new file mode 100644 index 000000000..d6c74ecd7 --- /dev/null +++ b/arch/i386/lib/realmode_switch.S @@ -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 + */ + + +/* 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/i386/lib/timer.c b/arch/i386/lib/timer.c new file mode 100644 index 000000000..5cb1f54fb --- /dev/null +++ b/arch/i386/lib/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 <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/i386/lib/video.c b/arch/i386/lib/video.c new file mode 100644 index 000000000..c58ed104c --- /dev/null +++ b/arch/i386/lib/video.c @@ -0,0 +1,237 @@ +/* + * (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/i386/lib/video_bios.c b/arch/i386/lib/video_bios.c new file mode 100644 index 000000000..c8060e60a --- /dev/null +++ b/arch/i386/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/i386/lib/zimage.c b/arch/i386/lib/zimage.c new file mode 100644 index 000000000..c3b4e597a --- /dev/null +++ b/arch/i386/lib/zimage.c @@ -0,0 +1,225 @@ +/* + * (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 i386 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> + +/* + * 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; + + +	setup_base = (void*)DEFAULT_SETUP_BASE;	/* base address for real-mode segment */ + +	if (KERNEL_MAGIC != *(u16*)(image + BOOT_FLAG_OFF)) { +		printf("Error: Invalid kernel magic (found 0x%04x, expected 0xaa55)\n", +		       *(u16*)(image + BOOT_FLAG_OFF)); +		return 0; +	} + + +	/* determine boot protocol version */ +	if (KERNEL_V2_MAGIC == *(u32*)(image+HEADER_OFF)) { +		bootproto = *(u16*)(image+VERSION_OFF); +	} else { +		/* Very old kernel */ +		bootproto = 0x0100; +	} + +	/* determine size of setup */ +	if (0 == *(u8*)(image + SETUP_SECTS_OFF)) { +		setup_size = 5 * 512; +	} else { +		setup_size = (*(u8*)(image + SETUP_SECTS_OFF) + 1) * 512; +	} + +	if (setup_size > SETUP_MAX_SIZE) { +		printf("Error: Setup is too large (%d bytes)\n", setup_size); +	} + +	/* Determine image type */ +	big_image = (bootproto >= 0x0200) && (*(u8*)(image + LOADFLAGS_OFF) & BIG_KERNEL_FLAG); + +	/* Derermine load address */ +	load_address = (void*)(big_image ? BZIMAGE_LOAD_ADDR:ZIMAGE_LOAD_ADDR); + +	/* load setup */ +	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); +	} + +	if (bootproto >= 0x0200) { +		*(u8*)(setup_base + TYPE_OF_LOADER_OFF) = 0xff; +		printf("Linux kernel version %s\n", +		       (char*)(setup_base + SETUP_START_OFFSET + +			       *(u16*)(setup_base + START_SYS_OFF + 2))); + +		if (initrd_addr) { +			printf("Initial RAM disk at linear address 0x%08lx, size %ld bytes\n", +			       initrd_addr, initrd_size); + +			*(u32*)(setup_base + RAMDISK_IMAGE_OFF) = initrd_addr; +			*(u32*)(setup_base + RAMDISK_SIZE_OFF)=initrd_size; +		} +	} + +	if (bootproto >= 0x0201) { +		*(u16*)(setup_base + HEAP_END_PTR_OFF) = HEAP_END_OFFSET; + +		/* CAN_USE_HEAP */ +		*(u8*)(setup_base + LOADFLAGS_OFF) = +			*(u8*)(setup_base + LOADFLAGS_OFF) | HEAP_FLAG; +	} + +	if (bootproto >= 0x0202) { +		*(u32*)(setup_base + CMD_LINE_PTR_OFF) = (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; +		*(u16*)(setup_base + SETUP_MOVE_SIZE_OFF) = 0x9100; +	} + + +	if (big_image) { +		if ((kernel_size - setup_size) > BZIMAGE_MAX_SIZE) { +			printf("Error: bzImage kernel too big! (size: %ld, max: %d)\n", +			       kernel_size - setup_size, BZIMAGE_MAX_SIZE); +			return 0; +		} + +	} else if ((kernel_size - setup_size) > ZIMAGE_MAX_SIZE) { +		printf("Error: zImage kernel too big! (size: %ld, max: %d)\n", +		       kernel_size - setup_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 - setup_size); + + +	memmove(load_address, image + setup_size, kernel_size - setup_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.xss = 0x9000; +	regs.esp = 0x9000; +	regs.eflags = 0; +	enter_realmode(((u32)setup_base+SETUP_START_OFFSET)>>4, 0, ®s, ®s); +} |