/*-------------------------------------------------------------------------+
| start.s v1.1 - PC386 BSP - 1997/08/07
+--------------------------------------------------------------------------+
| This file contains the entry point for the application.
| The name of this entry point is compiler dependent.
| It jumps to the BSP which is responsible for performing all initialization.
+--------------------------------------------------------------------------+
| (C) Copyright 1997 -
| - NavIST Group - Real-Time Distributed Systems and Industrial Automation
|
| http://pandora.ist.utl.pt
|
| Instituto Superior Tecnico * Lisboa * PORTUGAL
+--------------------------------------------------------------------------+
| Disclaimer:
|
| This file is provided "AS IS" without warranty of any kind, either
| expressed or implied.
+--------------------------------------------------------------------------+
| This code is based on an earlier generation RTEMS i386 start.s and the
| following copyright applies:
|
| **************************************************************************
| * COPYRIGHT (c) 1989-1997.
| * On-Line Applications Research Corporation (OAR).
| * Copyright assigned to U.S. Government, 1994.
| *
| * The license and distribution terms for this file may be
| * found in the file LICENSE in this distribution or at
| * http://www.OARcorp.com/rtems/license.html.
| **************************************************************************
|
| Also based on (from the Linux source tree):
| video.S - Copyright (C) 1995, 1996 Martin Mares <mj@k332.feld.cvut.cz>
|
| $Id$
+--------------------------------------------------------------------------*/
#include "asm.h"
/*----------------------------------------------------------------------------+
| Constants
+----------------------------------------------------------------------------*/
#ifdef pc386
.set PROT_CODE_SEG, 0x08 # offset of code segment descriptor into GDT
.set CR0_PE, 1 # protected mode flag on CR0 register
#endif /* pc386 */
/*----------------------------------------------------------------------------+
| A Descriptor table register has the following format:
+----------------------------------------------------------------------------*/
.set DTR_LIMIT, 0 # offset of two byte limit
.set DTR_BASE, 2 # offset of four byte base address
.set DTR_SIZE, 6 # size of DTR register
/*----------------------------------------------------------------------------+
| CODE section
+----------------------------------------------------------------------------*/
BEGIN_CODE
PUBLIC (start) # GNU default entry point
EXTERN (main)
EXTERN (load_segments)
EXTERN (exit)
SYM (start):
/*----------------------------------------------------------------------------+
| Switch VGA video to 80 lines x 50 columns mode. Has to be done before turning
| protected mode on since it uses BIOS int 10h (video) services.
+----------------------------------------------------------------------------*/
#if defined(pc386) && defined(RTEMS_VIDEO_80x50)
.code16
movw $0x0003, ax # forced set
int $0x10
movw $0x1112, ax # use 8x8 font
xorb %bl, %bl
int $0x10
movw $0x1201, ax # turn off cursor emulation
movb $0x34, %bl
int $0x10
movb $0x01, ah # define cursor (scan lines 0 to 7)
movw $0x0007, cx
int $0x10
.code32
#endif /* pc386 && RTEMS_VIDEO_80x50 */
nop
cli # DISABLE INTERRUPTS!!!
/*----------------------------------------------------------------------------+
| Bare PC machines boot in real mode! We have to turn protected mode on.
+----------------------------------------------------------------------------*/
#ifdef pc386
data16
movl $ SYM(gdtptr), eax
data16
andl $0x0000ffff, eax # get offset into segment
addr16
lgdt cs:(eax) # load Global Descriptor Table
data16
movl $ SYM(idtptr), eax
data16
andl $0x0000ffff, eax # get offset into segment
addr16
lidt cs:(eax) # load Interrupt Descriptor Table
movl %cr0, eax
data16
orl $CR0_PE, eax
movl eax, %cr0 # turn on protected mode
data16
ljmp $PROT_CODE_SEG, $ SYM(next) # flush prefetch queue
SYM(next):
#endif /* pc386 */
/*----------------------------------------------------------------------------+
| Load the segment registers (this is done by the board's BSP) and perform any
| other board specific initialization procedures.
|
| NOTE: Upon return, gs will contain the segment descriptor for a segment which
| maps directly to all of physical memory.
+----------------------------------------------------------------------------*/
jmp SYM (_load_segments) # load board dependent segments
/*----------------------------------------------------------------------------+
| Set up the stack
+----------------------------------------------------------------------------*/
PUBLIC (_establish_stack)
SYM (_establish_stack):
movl $_end, eax # eax = end of bss/start of heap
addl $heap_size, eax # eax = end of heap
movl eax, stack_start # Save for brk() routine
addl $stack_size, eax # make room for stack
andl $0xffffffc0, eax # align it on 16 byte boundary
movl eax, esp # set stack pointer
movl eax, ebp # set base pointer
/*----------------------------------------------------------------------------+
| Zero out the BSS segment
+----------------------------------------------------------------------------*/
SYM (zero_bss):
cld # make direction flag count up
movl $ SYM (_end), ecx # find end of .bss
movl $ SYM (_bss_start), edi # edi = beginning of .bss
subl edi, ecx # ecx = size of .bss in bytes
shll ecx # size of .bss in longs
xorl eax, eax # value to clear out memory
repne # while ecx != 0
stosl # clear a long in the bss
/*---------------------------------------------------------------------+
| Copy the Global Descriptor Table to our space
+---------------------------------------------------------------------*/
sgdt SYM (_Original_GDTR) # save original GDT
movzwl SYM (_Original_GDTR)+DTR_LIMIT, ecx # size of GDT in bytes;
# limit is 8192 entries * 8 bytes per
/*---------------------------------------------------------------------+
| make ds:esi point to the original GDT
+---------------------------------------------------------------------*/
movl SYM (_Original_GDTR)+DTR_BASE, esi
push ds # save ds
movw gs, ax
movw ax, ds
/*---------------------------------------------------------------------+
| make es:edi point to the new (our copy) GDT
+---------------------------------------------------------------------*/
movl $ SYM (_Global_descriptor_table), edi
rep
movsb # copy the GDT (ds:esi -> es:edi)
pop ds # restore ds
/*---------------------------------------------------------------------+
| Build and load new contents of GDTR
+---------------------------------------------------------------------*/
movw SYM (_Original_GDTR)+DTR_LIMIT, ecx # set new limit
movw cx, SYM (_New_GDTR)+DTR_LIMIT
push $ SYM (_Global_descriptor_table)
push es
call SYM (i386_Logical_to_physical)
addl $6, esp
movl eax, SYM (_New_GDTR)+DTR_BASE # set new base
cmpb $0, SYM (_Do_Load_GDT) # Should the new GDT be loaded?
je SYM (no_gdt_load) # NO, then branch
lgdt SYM (_New_GDTR) # load the new GDT
SYM (no_gdt_load):
/*---------------------------------------------------------------------+
| Copy the Interrupt Descriptor Table to our space
+---------------------------------------------------------------------*/
sidt SYM (_Original_IDTR) # save original IDT
movzwl SYM (_Original_IDTR)+DTR_LIMIT, ecx # size of IDT in bytes;
#limit is 256 entries * 8 bytes per
/*---------------------------------------------------------------------+
| make ds:esi point to the original IDT
+---------------------------------------------------------------------*/
movl SYM (_Original_IDTR)+DTR_BASE, esi
push ds # save ds
movw gs, ax
movw ax, ds
/*---------------------------------------------------------------------+
| make es:edi point to the new (our copy) IDT
+---------------------------------------------------------------------*/
movl $ SYM (Interrupt_descriptor_table), edi
rep
movsb # copy the IDT (ds:esi -> es:edi)
pop ds # restore ds
/*---------------------------------------------------------------------+
| Build and load new contents of IDTR
+---------------------------------------------------------------------*/
movw SYM (_Original_IDTR+DTR_LIMIT), ecx # set new limit
movw cx, SYM (_New_IDTR)+DTR_LIMIT
push $ SYM (Interrupt_descriptor_table)
push es
call SYM (i386_Logical_to_physical)
addl $6, esp
movl eax, SYM (_New_IDTR)+DTR_BASE # set new base
cmpb $0, SYM (_Do_Load_IDT) # Should the new IDT be loaded?
je SYM (no_idt_load) # NO, then branch
lidt SYM (_New_IDTR) # load the new IDT
SYM (no_idt_load):
/*---------------------------------------------------------------------+
| Initialize the i387.
|
| Using the NO WAIT form of the instruction insures that if it is not
| present the board will not lock up or get an exception.
+---------------------------------------------------------------------*/
fninit # MUST USE NO-WAIT FORM
/*---------------------------------------------------------------------+
| Transfer control to User's Board Support Package
+---------------------------------------------------------------------*/
pushl $0 # environp
pushl $0 # argv
pushl $0 # argc
call SYM (main)
addl $12, esp
/*---------------------------------------------------------------------+
| Clean up
+---------------------------------------------------------------------*/
EXTERN (return_to_monitor)
PUBLIC (Bsp_cleanup)
SYM (Bsp_cleanup):
cmpb $0, SYM (_Do_Load_IDT) # Was the new IDT loaded?
je SYM (no_idt_restore) # NO, then branch
lidt SYM (_Original_IDTR) # restore the new IDT
SYM (no_idt_restore):
cmpb $0, SYM (_Do_Load_GDT) # Was the new GDT loaded?
je SYM (no_gdt_restore) # NO, then branch
lgdt SYM (_Original_GDTR) # restore the new GDT
SYM (no_gdt_restore):
jmp SYM (_return_to_monitor)
END_CODE
/*----------------------------------------------------------------------------+
| DATA section
+----------------------------------------------------------------------------*/
BEGIN_DATA
#ifdef pc386
/**************************
* GLOBAL DESCRIPTOR TABLE *
**************************/
.align 4
SYM(gdtptr):
/* we use the NULL descriptor to store the GDT pointer - a trick quite
nifty due to: Robert Collins (rcollins@x86.org) */
.word gdtlen - 1
.long gdtptr
.word 0x0000
/* code segment */
.word 0xffff, 0
.byte 0, 0x9f, 0xcf, 0
/* data segment */
.word 0xffff, 0
.byte 0, 0x93, 0xcf, 0
.set gdtlen, . - gdtptr # length of GDT
/*************************************
* INTERRUPT DESCRIPTOR TABLE POINTER *
*************************************/
.align 4
SYM(idtptr):
.word 0x07ff # limit at maximum (allows all 256 interrupts)
.word 0, 0 # base at 0
#endif /* pc386 */
EXTERN (Do_Load_IDT) # defined in the BSP
EXTERN (Do_Load_GDT) # defined in the BSP
.align 2
PUBLIC (start_frame)
SYM (start_frame):
.long 0
PUBLIC (stack_start)
SYM (stack_start):
.long 0
END_DATA
/*----------------------------------------------------------------------------+
| BSS section
+----------------------------------------------------------------------------*/
BEGIN_BSS
PUBLIC (heap_size)
.set heap_size, 0x2000
PUBLIC (stack_size)
.set stack_size, 0x1000
PUBLIC (Interrupt_descriptor_table)
SYM (Interrupt_descriptor_table):
.space (256 * 8) # reserve space for all 256 interrupts
PUBLIC (_Original_IDTR)
SYM (_Original_IDTR):
.space DTR_SIZE
PUBLIC (_New_IDTR)
SYM (_New_IDTR):
.space DTR_SIZE
PUBLIC (_Global_descriptor_table)
SYM (_Global_descriptor_table):
#ifdef pc386
.space (3 * 8) # the PC386 bsp only needs 3 segment descriptors:
#else # NULL, CODE and DATA
.space (8192 * 8)
#endif /* pc386 */
PUBLIC (_Original_GDTR)
SYM (_Original_GDTR):
.space DTR_SIZE
PUBLIC (_New_GDTR)
SYM (_New_GDTR):
.space DTR_SIZE
PUBLIC (_Physical_base_of_ds)
SYM (_Physical_base_of_ds):
.space 4
PUBLIC (_Physical_base_of_cs)
SYM (_Physical_base_of_cs):
.space 4
END_BSS
END