while others dislike them being extra commits, let's clean them up once and for all for the existing code. If it's ugly, let it only be ugly once :-) Signed-off-by: Stefan Reinauer <stepan@coresystems.de> Acked-by: Stefan Reinauer <stepan@coresystems.de> git-svn-id: svn://svn.coreboot.org/coreboot/trunk@5507 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
		
			
				
	
	
		
			582 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
			
		
		
	
	
			582 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
| /*
 | |
|  * exec_kernel/user_space/head.S
 | |
|  *
 | |
|  * Copyright (C) 2000, 2002, 2003 Eric Biederman
 | |
|  *
 | |
|  * Parts of this code were take from the linux startup
 | |
|  * code of linux-2.4.0-test9
 | |
|  *
 | |
|  * Other parts were taken from etherboot-5.0.5
 | |
|  */
 | |
| 
 | |
| #define ASSEMBLY 1
 | |
| 
 | |
| #define RELOC 0x10000
 | |
| #define PROT_CODE_SEG 0x10
 | |
| #define PROT_DATA_SEG 0x18
 | |
| #define REAL_CODE_SEG 0x08
 | |
| #define REAL_DATA_SEG 0x20
 | |
| 
 | |
| 	.equ	CR0_PE,1
 | |
| 
 | |
| .text
 | |
| .code32
 | |
| 
 | |
| 
 | |
| #include "convert.h"
 | |
| 
 | |
| 	.globl startup_32
 | |
| startup_32:
 | |
| 	cld
 | |
| 	cli
 | |
| 
 | |
| 	# Save the arguments safely out of the way
 | |
| 	movl	%eax, boot_type
 | |
| 	movl	%ebx, boot_data
 | |
| 	cmp	$0,%esp
 | |
| 	jz	1f
 | |
| 	movl	4(%esp), %eax
 | |
| 	movl	%eax, boot_param
 | |
| 1:
 | |
| 
 | |
| 	movl stack_start, %esp
 | |
| 
 | |
| 	# Clear eflags
 | |
| 	pushl $0
 | |
| 	popfl
 | |
| 
 | |
| 	# Clear BSS
 | |
| 	xorl %eax,%eax
 | |
| 	movl $ _edata,%edi
 | |
| 	movl $ _end,%ecx
 | |
| 	subl %edi,%ecx
 | |
| 	cld
 | |
| 	rep
 | |
| 	stosb
 | |
| 
 | |
| 	# Move the gdt where Linux will not smash it during decompression
 | |
| 	movl	$gdt, %esi
 | |
| 	movl	$GDTLOC, %edi
 | |
| 	movl	$(gdt_end - gdt), %ecx
 | |
| 	rep	movsb
 | |
| 
 | |
| 	# Linux makes stupid assumptions about the segments
 | |
| 	# that are already setup, so setup a new gdt & ldt
 | |
| 	# and then reload the segment registers.
 | |
| 
 | |
| 	lgdt	gdt_48
 | |
| 	lidt	idt_48
 | |
| 
 | |
| 	# Load the data segment registers
 | |
| 	movl	$ PROT_DATA_SEG, %eax
 | |
| 	movl	%eax, %ds
 | |
| 	movl	%eax, %es
 | |
| 	movl	%eax, %fs
 | |
| 	movl	%eax, %gs
 | |
| 	movl    %eax, %ss
 | |
| 
 | |
| 	pushl	$image_params	# image build time parameters as forth arg
 | |
| 	pushl	boot_param	# boot_param pointer as third arg
 | |
| 	pushl	boot_data	# boot data pointer as second arg
 | |
| 	pushl	boot_type	# boot data type as first argument
 | |
| 	call	convert_params
 | |
| 
 | |
| 	movl	%eax, %esi	# put the real mode pointer in a safe place
 | |
| 	addl	$16, %esp	# pop the arguments
 | |
| 
 | |
| 
 | |
| 	# Setup the registers before jumping to linux
 | |
| 
 | |
| 
 | |
| 	# clear eflags
 | |
| 	pushl	$0
 | |
| 	popfl
 | |
| 
 | |
| 	# Flag to indicate we are the bootstrap processor
 | |
| 	xorl	%ebx, %ebx
 | |
| 
 | |
| 	movl    switch_64, %eax
 | |
| 	cmp	$1, %eax
 | |
| 	jz	switch_to_64
 | |
| 
 | |
| 	# Clear the unspecified registers for good measure
 | |
| 	xorl	%eax, %eax
 | |
| 	xorl	%ecx, %ecx
 | |
| 	xorl	%edx, %edx
 | |
| 	xorl	%edi, %edi
 | |
| 	xorl	%ebp, %ebp
 | |
| 
 | |
| 	# do not clear esp, we still need to use lret later
 | |
| 
 | |
|         pushl $PROT_CODE_SEG
 | |
|         movl entry, %eax
 | |
|         pushl %eax
 | |
| 
 | |
| 	lret
 | |
| 
 | |
| switch_to_64:
 | |
| 
 | |
| 	/* We need to switch to 64bit before use startup_64 entry go to kernel */
 | |
|  /*
 | |
|   * Prepare for entering 64 bit mode
 | |
|   */
 | |
|         # Move the gdt64 where Linux will not smash it during decompression
 | |
| 	movl	%esi, %eax # save the real mode pointer
 | |
| 	movl    $gdt64, %esi
 | |
| 	movl    $GDT64LOC, %edi
 | |
| 	movl    $(gdt64_end - gdt64), %ecx
 | |
| 	rep     movsb
 | |
| 	movl	%eax, %esi
 | |
| 
 | |
| 	/* Load new GDT with the 64bit segments using 32bit descriptor */
 | |
| 	lgdt	gdt64
 | |
| 
 | |
| 	/* Enable PAE mode */
 | |
| 	xorl    %eax, %eax
 | |
| 	btsl    $5, %eax
 | |
| 	movl    %eax, %cr4
 | |
| 
 | |
|  /*
 | |
|   * Build early 4G boot pagetable
 | |
|   */
 | |
|        /* Initialize Page tables to 0*/
 | |
|        movl    $PGTLOC, %edi
 | |
|        xorl    %eax, %eax
 | |
|        movl    $((4096*6)/4), %ecx
 | |
|        rep     stosl
 | |
| 
 | |
|        /* Build Level 4 */
 | |
|        movl    $(PGTLOC + 0), %edi
 | |
|        leal    0x1007 (%edi), %eax
 | |
|        movl    %eax, 0(%edi)
 | |
| 
 | |
|        /* Build Level 3 */
 | |
|        movl    $(PGTLOC + 0x1000), %edi
 | |
|        leal    0x1007(%edi), %eax
 | |
|        movl    $4, %ecx
 | |
| 1:     movl    %eax, 0x00(%edi)
 | |
|        addl    $0x00001000, %eax
 | |
|        addl    $8, %edi
 | |
|        decl    %ecx
 | |
|        jnz     1b
 | |
| 
 | |
|        /* Build Level 2 */
 | |
|        movl    $(PGTLOC + 0x2000), %edi
 | |
|        movl    $0x00000183, %eax
 | |
|        movl    $2048, %ecx
 | |
| 1:     movl    %eax, 0(%edi)
 | |
|        addl    $0x00200000, %eax
 | |
|        addl    $8, %edi
 | |
|        decl    %ecx
 | |
|        jnz     1b
 | |
| 
 | |
|        /* Enable the boot page tables */
 | |
|        movl    $PGTLOC, %eax
 | |
|        movl    %eax, %cr3
 | |
| 
 | |
|        /* Enable Long mode in EFER (Extended Feature Enable Register) */
 | |
|        movl    $0xc0000080, %ecx
 | |
|        rdmsr
 | |
|        btsl    $8, %eax
 | |
|        wrmsr
 | |
| 
 | |
| 	/* Preparing for 64bit jmp */
 | |
|         pushl $PROT_CODE_SEG
 | |
|         movl entry, %eax
 | |
|         pushl %eax
 | |
| 
 | |
|        /* Enter paged protected Mode, activating Long Mode */
 | |
|         xorl    %eax, %eax
 | |
|         btsl    $31, %eax
 | |
|         btsl    $0, %eax
 | |
|         movl    %eax, %cr0
 | |
| 
 | |
|         /*
 | |
|          * At this point we're in long mode but in 32bit compatibility mode
 | |
|          * with EFER.LME = 1, CS.L = 0, CS.D = 1 (and in turn
 | |
|          * EFER.LMA = 1). Now we want to jump in 64bit mode, to do that we use
 | |
|          * the new gdt/idt that has __KERNEL_CS with CS.L = 1.
 | |
|          */
 | |
| 
 | |
| 	lret
 | |
| 
 | |
| 
 | |
| 	/* Routines to query the BIOS... */
 | |
| /**************************************************************************
 | |
| E820_MEMSIZE - Get a listing of memory regions
 | |
| **************************************************************************/
 | |
| #define SMAP	0x534d4150
 | |
| 	.globl	meme820
 | |
| meme820:
 | |
| 	pushl	%ebp
 | |
| 	movl	%esp, %ebp
 | |
| 	pushl	%ebx
 | |
| 	pushl	%esi
 | |
| 	pushl	%edi
 | |
| 	movl	8(%ebp), %edi	/* Address to return e820 structures at */
 | |
| 	subl	$RELOC, %edi
 | |
| 	movl	12(%ebp), %esi	/* Maximum number of e820 structurs to return */
 | |
| 	pushl	%esi
 | |
| 	call	_prot_to_real
 | |
| 	.code16
 | |
| 	xorl	%ebx, %ebx
 | |
| jmpe820:
 | |
| 	movl	$0xe820, %eax
 | |
| 	movl	$SMAP, %edx
 | |
| 	movl	$20, %ecx
 | |
| 	/* %di was setup earlier */
 | |
| 	int	$0x15
 | |
| 	jc	bail820
 | |
| 
 | |
| 	cmpl	$SMAP, %eax
 | |
| 	jne	bail820
 | |
| 
 | |
| good820:
 | |
| 	/* If this is useable memory, we save it by simply advancing %di by
 | |
| 	 * sizeof(e820rec)
 | |
| 	 */
 | |
| 	decl	%esi
 | |
| 	testl	%esi,%esi
 | |
| 	jz	bail820
 | |
| 
 | |
| 	addw	$20, %di
 | |
| again820:
 | |
| 	cmpl	$0, %ebx	/* check to see if %ebx is set to EOF */
 | |
| 	jne	jmpe820
 | |
| 
 | |
| bail820:
 | |
| 	data32 call	_real_to_prot
 | |
| 	.code32
 | |
| 	popl	%eax
 | |
| 	subl	%esi, %eax	/* Compute how many structure we read */
 | |
| 
 | |
| 	/* Restore everything else */
 | |
| 	popl	%edi
 | |
| 	popl	%esi
 | |
| 	popl	%ebx
 | |
| 	movl	%ebp, %esp
 | |
| 	popl	%ebp
 | |
| 	ret
 | |
| 
 | |
| 
 | |
| /**************************************************************************
 | |
| MEME801 - Determine size of extended memory
 | |
| **************************************************************************/
 | |
| 	.globl meme801
 | |
| meme801:
 | |
| 	pushl	%ebx
 | |
| 	pushl	%esi
 | |
| 	pushl	%edi
 | |
| 	call	_prot_to_real
 | |
| 	.code16
 | |
| 
 | |
| 	stc					# fix to work around buggy
 | |
| 	xorw	%cx,%cx				# BIOSes which dont clear/set
 | |
| 	xorw	%dx,%dx				# carry on pass/error of
 | |
| 						# e801h memory size call
 | |
| 						# or merely pass cx,dx though
 | |
| 						# without changing them.
 | |
| 	movw	$0xe801,%ax
 | |
| 	int	$0x15
 | |
| 	jc	e801absent
 | |
| 
 | |
| 	cmpw	$0x0, %cx			# Kludge to handle BIOSes
 | |
| 	jne	e801usecxdx			# which report their extended
 | |
| 	cmpw	$0x0, %dx			# memory in AX/BX rather than
 | |
| 	jne	e801usecxdx			# CX/DX.  The spec I have read
 | |
| 	movw	%ax, %cx			# seems to indicate AX/BX
 | |
| 	movw	%bx, %dx			# are more reasonable anyway...
 | |
| 
 | |
| e801usecxdx:
 | |
| 	andl	$0xffff, %edx			# clear sign extend
 | |
| 	shll	$6, %edx			# and go from 64k to 1k chunks
 | |
| 	movl	%edx, %eax			# store extended memory size
 | |
| 	andl	$0xffff, %ecx			# clear sign extend
 | |
|  	addl	%ecx, %eax			# and add lower memory into
 | |
| 
 | |
| 	jmp	e801out
 | |
| e801absent:
 | |
| 	xorl	%eax,%eax
 | |
| 
 | |
| e801out:
 | |
| 	data32 call	_real_to_prot
 | |
| 	.code32
 | |
| 	/* Restore Everything */
 | |
| 	popl	%edi
 | |
| 	popl	%esi
 | |
| 	popl	%ebx
 | |
| 	ret
 | |
| 
 | |
| /**************************************************************************
 | |
| MEM88 - Determine size of extended memory
 | |
| **************************************************************************/
 | |
| 	.globl mem88
 | |
| mem88:
 | |
| 	pushl	%ebx
 | |
| 	pushl	%esi
 | |
| 	pushl	%edi
 | |
| 	call	_prot_to_real
 | |
| 	.code16
 | |
| 
 | |
| 	movb	$0x88, %ah
 | |
| 	int	$0x15
 | |
| 	andl	$0xffff, %eax
 | |
| 
 | |
| 	data32 call	_real_to_prot
 | |
| 	.code32
 | |
| 
 | |
| 	/* Restore Everything */
 | |
| 	popl	%edi
 | |
| 	popl	%esi
 | |
| 	popl	%ebx
 | |
| 	ret
 | |
| 
 | |
| 
 | |
| /**************************************************************************
 | |
| BASEMEMSIZE - Get size of the conventional (base) memory
 | |
| **************************************************************************/
 | |
| 	.globl	basememsize
 | |
| basememsize:
 | |
| 	call	_prot_to_real
 | |
| 	.code16
 | |
| 	int	$0x12
 | |
| 	movw	%ax,%cx
 | |
| 	DATA32 call	_real_to_prot
 | |
| 	.code32
 | |
| 	movw	%cx,%ax
 | |
| 	ret
 | |
| 
 | |
| /**************************************************************************
 | |
| _REAL_TO_PROT - Go from REAL mode to Protected Mode
 | |
| **************************************************************************/
 | |
| 	.globl	_real_to_prot
 | |
| _real_to_prot:
 | |
| 	.code16
 | |
| 	cli
 | |
| 	cs
 | |
| 	addr32 lgdt	gdt_48 - RELOC
 | |
| 	movl	%cr0,%eax
 | |
| 	orl	$CR0_PE,%eax
 | |
| 	movl	%eax,%cr0		/* turn on protected mode */
 | |
| 
 | |
| 	/* flush prefetch queue, and reload %cs:%eip */
 | |
| 	data32 ljmp	$PROT_CODE_SEG,$1f
 | |
| 1:
 | |
| 	.code32
 | |
| 	/* reload other segment registers */
 | |
| 	movl	$PROT_DATA_SEG,%eax
 | |
| 	movl	%eax,%ds
 | |
| 	movl	%eax,%es
 | |
| 	movl	%eax,%ss
 | |
| 	addl	$RELOC,%esp		/* Fix up stack pointer */
 | |
| 	xorl	%eax,%eax
 | |
| 	movl	%eax,%fs
 | |
| 	movl	%eax,%gs
 | |
| 	popl	%eax			/* Fix up return address */
 | |
| 	addl	$RELOC,%eax
 | |
| 	pushl	%eax
 | |
| 
 | |
| 	/* switch to protected mode idt */
 | |
| 	cs
 | |
| 	lidt	idt_48
 | |
| 	ret
 | |
| 
 | |
| /**************************************************************************
 | |
| _PROT_TO_REAL - Go from Protected Mode to REAL Mode
 | |
| **************************************************************************/
 | |
| 	.globl	_prot_to_real
 | |
| _prot_to_real:
 | |
| 	.code32
 | |
| 	popl	%eax
 | |
| 	subl	$RELOC,%eax		/* Adjust return address */
 | |
| 	pushl	%eax
 | |
| 	subl	$RELOC,%esp		/* Adjust stack pointer */
 | |
| 	ljmp	$REAL_CODE_SEG,$1f- RELOC	/* jump to a 16 bit segment */
 | |
| 1:
 | |
| 	.code16
 | |
| 	/* clear the PE bit of CR0 */
 | |
| 	movl	%cr0,%eax
 | |
| 	andl	$0!CR0_PE,%eax
 | |
| 	movl	%eax,%cr0
 | |
| 
 | |
| 	/* make intersegment jmp to flush the processor pipeline
 | |
| 	 * and reload %cs:%eip (to clear upper 16 bits of %eip).
 | |
| 	 */
 | |
| 	data32 ljmp	$(RELOC)>>4,$2f- RELOC
 | |
| 2:
 | |
| 	/* we are in real mode now
 | |
| 	 * set up the real mode segment registers : %ds, $ss, %es
 | |
| 	 */
 | |
| 	movw	%cs,%ax
 | |
| 	movw	%ax,%ds
 | |
| 	movw	%ax,%es
 | |
| 	movw	%ax,%ss
 | |
| 	movw	%ax,%fs
 | |
| 	movw	%ax,%gs
 | |
| 
 | |
| 	/* Switch to the real mode idt */
 | |
| 	cs
 | |
| 	addr32	lidt	idt_real - RELOC
 | |
| 
 | |
| 	sti
 | |
| 	data32 ret	/* There is a 32 bit return address on the stack */
 | |
| 	.code32
 | |
| 
 | |
| boot_type:	.long 0
 | |
| boot_data:	.long 0
 | |
| boot_param:	.long 0
 | |
| 
 | |
| idt_real:
 | |
| 	.word	0x400				# idt limit = 256
 | |
| 	.word	0, 0
 | |
| idt_48:
 | |
| 	.word	0				# idt limit = 0
 | |
| 	.word	0, 0				# idt base = 0L
 | |
| gdt_48:
 | |
| 	.word	gdt_end - gdt - 1		# gdt limit=40,
 | |
| 						# (5 GDT entries)
 | |
| 	.long   GDTLOC				# gdt base
 | |
| 
 | |
| # Descriptor tables
 | |
| # These need to be in a seperate section so I can be
 | |
| # certain later activities dont stomp them
 | |
| gdt:
 | |
| 	/* 0x00 */
 | |
| 	.word	0, 0, 0, 0			# dummy
 | |
| 
 | |
| 	/* 0x08 */
 | |
| 	/* 16 bit real mode code segment */
 | |
| 	.word	0xffff,(RELOC&0xffff)
 | |
| 	.byte	(RELOC>>16),0x9b,0x00,(RELOC>>24)
 | |
| 
 | |
| 	/* 0x10 */
 | |
| 	.word	0xFFFF				# 4Gb - (0x100000*0x1000 = 4Gb)
 | |
| 	.word	0				# base address = 0
 | |
| 	.word	0x9A00				# code read/exec
 | |
| 	.word	0x00CF				# granularity = 4096, 386
 | |
| 						#  (+5th nibble of limit)
 | |
| 
 | |
| 	/* 0x18 */
 | |
| 	.word	0xFFFF				# 4Gb - (0x100000*0x1000 = 4Gb)
 | |
| 	.word	0				# base address = 0
 | |
| 	.word	0x9200				# data read/write
 | |
| 	.word	0x00CF				# granularity = 4096, 386
 | |
| 						#  (+5th nibble of limit)
 | |
| 
 | |
| 	/* 0x20 */
 | |
| 	/* 16 bit real mode data segment */
 | |
| 	.word	0xffff,(RELOC&0xffff)
 | |
| 	.byte	(RELOC>>16),0x93,0x00,(RELOC>>24)
 | |
| 
 | |
| 	/* For 2.5.x the kernel segments have moved */
 | |
| 
 | |
| 	/* 0x28 dummy */
 | |
| 	.quad	0
 | |
| 
 | |
| 	/* 0x30 dummy */
 | |
| 	.quad	0
 | |
| 	/* 0x38 dummy */
 | |
| 	.quad	0
 | |
| 	/* 0x40 dummy */
 | |
| 	.quad	0
 | |
| 	/* 0x48 dummy */
 | |
| 	.quad	0
 | |
| 	/* 0x50 dummy */
 | |
| 	.quad	0
 | |
| 	/* 0x58 dummy */
 | |
| 	.quad	0
 | |
| 
 | |
| 
 | |
| 	/* 0x60 */
 | |
| 	.word	0xFFFF				# 4Gb - (0x100000*0x1000 = 4Gb)
 | |
| 	.word	0				# base address = 0
 | |
| 	.word	0x9A00				# code read/exec
 | |
| 	.word	0x00CF				# granularity = 4096, 386
 | |
| 						#  (+5th nibble of limit)
 | |
| 
 | |
| 	/* 0x68 */
 | |
| 	.word	0xFFFF				# 4Gb - (0x100000*0x1000 = 4Gb)
 | |
| 	.word	0				# base address = 0
 | |
| 	.word	0x9200				# data read/write
 | |
| 	.word	0x00CF				# granularity = 4096, 386
 | |
| 						#  (+5th nibble of limit)
 | |
| 
 | |
| /*
 | |
|  * The layout of the per-CPU GDT under Linux:
 | |
|  *
 | |
|  *   0 - null
 | |
|  *   1 - reserved
 | |
|  *   2 - reserved
 | |
|  *   3 - reserved
 | |
|  *
 | |
|  *   4 - default user CS                <==== new cacheline
 | |
|  *   5 - default user DS
 | |
|  *
 | |
|  *  ------- start of TLS (Thread-Local Storage) segments:
 | |
|  *
 | |
|  *   6 - TLS segment #1                 [ glibc's TLS segment ]
 | |
|  *   7 - TLS segment #2                 [ Wine's %fs Win32 segment ]
 | |
|  *   8 - TLS segment #3
 | |
|  *   9 - reserved
 | |
|  *  10 - reserved
 | |
|  *  11 - reserved
 | |
|  *
 | |
|  *  ------- start of kernel segments:
 | |
|  *
 | |
|  *  12 - kernel code segment            <==== new cacheline
 | |
|  *  13 - kernel data segment
 | |
|  *  14 - TSS
 | |
|  *  15 - LDT
 | |
|  *  16 - PNPBIOS support (16->32 gate)
 | |
|  *  17 - PNPBIOS support
 | |
|  *  18 - PNPBIOS support
 | |
|  *  19 - PNPBIOS support
 | |
|  *  20 - PNPBIOS support
 | |
|  *  21 - APM BIOS support
 | |
|  *  22 - APM BIOS support
 | |
|  *  23 - APM BIOS support
 | |
|  */
 | |
| gdt_end:
 | |
| 
 | |
| gdt64:
 | |
| 	.word	gdt64_end - gdt64
 | |
| 	.long	GDT64LOC
 | |
| 	.word	0
 | |
| 	.quad	0x0000000000000000	/* NULL descriptor */
 | |
| 	.quad	0x00af9a000000ffff	/* __KERNEL_CS */
 | |
| 	.quad	0x00cf92000000ffff	/* __KERNEL_DS */
 | |
| gdt64_end:
 | |
| 
 | |
| 	.section ".trailer", "a"
 | |
| 	/* Constants set at build time, these are at the very end of my image */
 | |
| 	.balign 16
 | |
| 	.global image_params
 | |
| image_params:
 | |
| 
 | |
| convert_magic:
 | |
| 	.long	CONVERT_MAGIC
 | |
| gdt_size:
 | |
| 	.long	gdt_end - gdt
 | |
| gdt64_size:
 | |
| 	.long	gdt64_end - gdt64
 | |
| pgt_size:
 | |
| 	.long   4096*6
 | |
| bss_size:
 | |
| 	.long	bss_sizex
 | |
| ramdisk_flags:
 | |
| 	.word	0
 | |
| root_dev:
 | |
| 	.word	DEFAULT_ROOT_DEV
 | |
| entry:
 | |
| 	.long	0
 | |
| switch_64:
 | |
| 	.long	0
 | |
| initrd_start:
 | |
| 	.long	0
 | |
| initrd_size:
 | |
| 	.long	0
 | |
| cmdline:
 | |
| 	.asciz "BOOT_IMAGE=head.S console=ttyS0 ip=dhcp root=/dev/nfs"
 | |
| 	.org cmdline + CMDLINE_MAX, 0
 | |
| cmdline_end:
 |