477 lines
9.9 KiB
ArmAsm
477 lines
9.9 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
|
|
|
|
# Clear the unspecified registers for good measure
|
|
xorl %eax, %eax
|
|
xorl %ecx, %ecx
|
|
xorl %edx, %edx
|
|
xorl %edi, %edi
|
|
xorl %esp, %esp
|
|
xorl %ebp, %ebp
|
|
|
|
|
|
# Jump to the linux kernel
|
|
ljmp $ PROT_CODE_SEG , $ 0x100000
|
|
|
|
|
|
/* 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:
|
|
|
|
|
|
|
|
.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
|
|
bss_size:
|
|
.long bss_size
|
|
ramdisk_flags:
|
|
.word 0
|
|
root_dev:
|
|
.word DEFAULT_ROOT_DEV
|
|
entry:
|
|
.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:
|