Updated.
[jlayton/glibc.git] / sysdeps / standalone / i386 / start.S
1 /* Copyright (C) 1994, 1997 Free Software Foundation, Inc.
2    Contributed by Joel Sherrill (jsherril@redstone-emh2.army.mil),
3      On-Line Applications Research Corporation.
4    This file is part of the GNU C Library.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public
17    License along with the GNU C Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 /*  entry.s
22  *
23  *  This file contains the entry point for the application.
24  *  The name of this entry point is compiler dependent.
25  *  It jumps to the BSP which is responsible for performing
26  *  all initialization.
27  *
28  */
29
30         .data
31         .global  _Do_Load_IDT
32         .global  _Do_Load_GDT
33
34         .text
35               .global  start                  # GNU default entry point
36         .global  _establish_stack
37
38         .global   _bsp_start
39         .global   _load_segments
40         .global   __exit
41
42 start:
43         nop
44         cli                             # DISABLE INTERRUPTS!!!
45 #
46 #  Load the segment registers
47 #
48 #  NOTE: Upon return, gs will contain the segment descriptor for
49 #        a segment which maps directly to all of physical memory.
50 #
51         jmp     _load_segments          # load board dependent segments
52
53 #
54 #  Set up the stack
55 #
56
57 _establish_stack:
58
59         movl    $stack_end,%esp         # set stack pointer
60         movl    $stack_end,%ebp         # set base pointer
61
62 #
63 #  Zero out the BSS segment
64 #
65 zero_bss:
66         cld                             # make direction flag count up
67         movl    $_end,%ecx              # find end of .bss
68         movl    $_bss_start,%edi        # edi = beginning of .bss
69         subl    %edi,%ecx               # ecx = size of .bss in bytes
70         shrl    $2,%ecx                 # size of .bss in longs
71         xorl    %eax,%eax               # value to clear out memory
72         repne                           # while ecx != 0
73         stosl                           #   clear a long in the bss
74
75 #
76 #  Set the C heap information for malloc
77 #
78         movl    $heap_size,___C_heap_size    # set ___C_heap_size
79         movl    $heap_memory,___C_heap_start # set ___C_heap_start
80
81 #
82 #  Copy the Global Descriptor Table to our space
83 #
84
85         sgdt    _Original_GDTR          # save original GDT
86         movzwl  _Original_GDTR_limit,%ecx # size of GDT in bytes; limit
87                                           #   is 8192 entries * 8 bytes per
88
89         # make ds:esi point to the original GDT
90
91         movl    _Original_GDTR_base,%esi
92         push    %ds                     # save ds
93         movw    %gs,%ax
94         movw    %ax,%ds
95
96         # make es:edi point to the new (our copy) GDT
97         movl    $_Global_descriptor_table,%edi
98
99         rep
100         movsb                            # copy the GDT (ds:esi -> es:edi)
101
102         pop     %ds                      # restore ds
103
104         # Build and load new contents of GDTR
105         movw    _Original_GDTR_limit,%ecx # set new limit
106         movw    %cx,_New_GDTR_limit
107
108         push    $_Global_descriptor_table
109         push    %es
110         call    _Logical_to_physical
111         addl    $6,%esp
112         movl    %eax,_New_GDTR_base      # set new base
113
114         cmpb    $0,_Do_Load_GDT          # Should the new GDT be loaded?
115         je      no_gdt_load              # NO, then branch
116         lgdt    _New_GDTR                # load the new GDT
117 no_gdt_load:
118
119 #
120 #  Copy the Interrupt Descriptor Table to our space
121 #
122
123         sidt    _Original_IDTR          # save original IDT
124         movzwl  _Original_IDTR_limit,%ecx # size of IDT in bytes; limit
125                                           #   is 256 entries * 8 bytes per
126
127
128         # make ds:esi point to the original IDT
129         movl    _Original_IDTR_base,%esi
130
131         push    %ds                     # save ds
132         movw    %gs,%ax
133         movw    %ax,%ds
134
135         # make es:edi point to the new (our copy) IDT
136         movl    $_Interrupt_descriptor_table,%edi
137
138         rep
139         movsb                            # copy the IDT (ds:esi -> es:edi)
140         pop     %ds                      # restore ds
141
142         # Build and load new contents of IDTR
143         movw    _Original_IDTR_limit,%ecx # set new limit
144         movw    %cx,_New_IDTR_limit
145
146         push    $_Interrupt_descriptor_table
147         push    %es
148         call    _Logical_to_physical
149         addl    $6,%esp
150         movl    %eax,_New_IDTR_base      # set new base
151
152         cmpb    $0,_Do_Load_IDT          # Should the new IDT be loaded?
153         je      no_idt_load              # NO, then branch
154         lidt    _New_IDTR                # load the new IDT
155 no_idt_load:
156
157 #
158 #  Initialize the i387.
159 #
160 #  Using the NO WAIT form of the instruction insures that if
161 #  it is not present the board will not lock up or get an
162 #  exception.
163 #
164
165         fninit                           # MUST USE NO-WAIT FORM
166
167         call    __Board_Initialize       # initialize the board
168
169         pushl   $0                       # envp = NULL
170         pushl   $0                       # argv = NULL
171         pushl   $0                       # argc = NULL
172         call    ___libc_init             # initialize the library and
173                                          #   call main
174         addl    $12,%esp
175
176         pushl   $0                       # argc = NULL
177         call    __exit                   # call the Board specific exit
178         addl     $4,%esp
179
180 #
181 #  Clean up
182 #
183
184
185         .global  _Bsp_cleanup
186
187         .global   _return_to_monitor
188
189 _Bsp_cleanup:
190         cmpb    $0,_Do_Load_IDT          # Was the new IDT loaded?
191         je      no_idt_restore           # NO, then branch
192         lidt    _Original_IDTR           # restore the new IDT
193 no_idt_restore:
194
195         cmpb    $0,_Do_Load_GDT          # Was the new GDT loaded?
196         je      no_gdt_restore           # NO, then branch
197         lgdt    _Original_GDTR           # restore the new GDT
198 no_gdt_restore:
199         jmp     _return_to_monitor
200
201 #
202 #  void *Logical_to_physical(
203 #     rtems_unsigned16  segment,
204 #     void             *address
205 #  );
206 #
207 #  Returns thirty-two bit physical address for segment:address.
208 #
209
210         .global  _Logical_to_physical
211
212 .set SEGMENT_ARG, 4
213 .set ADDRESS_ARG, 8
214
215 _Logical_to_physical:
216
217         xorl    %eax,%eax                # clear eax
218         movzwl  SEGMENT_ARG(%esp),%ecx   # ecx = segment value
219         movl    $_Global_descriptor_table,%edx # edx = address of our GDT
220         addl    %ecx,%edx                # edx = address of desired entry
221         movb    7(%edx),%ah              # ah = base 31:24
222         movb    4(%edx),%al              # al = base 23:16
223         shll    $16,%eax                 # move ax into correct bits
224         movw    2(%edx),%ax              # ax = base 0:15
225         movl    ADDRESS_ARG(%esp),%ecx   # ecx = address to convert
226         addl    %eax,%ecx                # ecx = physical address equivalent
227         movl    %ecx,%eax                # eax = ecx
228         ret
229
230 #
231 #  void *Physical_to_logical(
232 #     rtems_unsigned16  segment,
233 #     void             *address
234 #  );
235 #
236 #  Returns thirty-two bit physical address for segment:address.
237 #
238
239         .global  _Physical_to_logical
240
241 #.set SEGMENT_ARG, 4
242 #.set ADDRESS_ARG, 8   -- use sets from above
243
244 _Physical_to_logical:
245
246         xorl    %eax,%eax                # clear eax
247         movzwl  SEGMENT_ARG(%esp),%ecx   # ecx = segment value
248         movl    $_Global_descriptor_table,%edx # edx = address of our GDT
249         addl    %ecx,%edx                # edx = address of desired entry
250         movb    7(%edx),%ah              # ah = base 31:24
251         movb    4(%edx),%al              # al = base 23:16
252         shll    $16,%eax                 # move ax into correct bits
253         movw    2(%edx),%ax              # ax = base 0:15
254         movl    ADDRESS_ARG(%esp),%ecx   # ecx = address to convert
255         subl    %eax,%ecx                # ecx = logical address equivalent
256         movl    %ecx,%eax                # eax = ecx
257         ret
258
259
260 /*
261  *  Data Declarations.  Start with a macro which helps declare space.
262  */
263
264         .bss
265
266 #define DECLARE_SPACE(_name,_space,_align) \
267           .globl   _name ; \
268           .align   _align ; \
269 _name##:  .space _space
270
271 #define DECLARE_LABEL(_name) \
272           .globl   _name ; \
273 _name##:
274
275 #define DECLARE_PTR(_name) DECLARE_SPACE(_name,4,2)
276 #define DECLARE_U32(_name) DECLARE_SPACE(_name,4,2)
277 #define DECLARE_U16(_name) DECLARE_SPACE(_name,2,1)
278
279 /*
280  *  Require environment stuff
281  */
282
283 DECLARE_LABEL(_environ)
284 DECLARE_PTR(environ)
285
286 DECLARE_LABEL(_errno)
287 DECLARE_U32(errno)
288
289 /*
290  *  Miscellaneous Variables used to restore the CPU state.
291  *
292  *  Start with a macro to declare the space for the contents of
293  *  a Descriptor Table register.
294  */
295
296 #define DECLARE_DTR_SPACE(_name) \
297           .global   _name ; \
298           .align    4 ; \
299 _name##:  ; \
300 _name##_limit:  .space 2  ; \
301 _name##_base:   .space 4
302
303 DECLARE_SPACE(_Interrupt_descriptor_table,256*8,4)
304 DECLARE_SPACE(_Global_descriptor_table,8192*8,4)
305
306 DECLARE_DTR_SPACE(_Original_IDTR)
307 DECLARE_DTR_SPACE(_New_IDTR)
308 DECLARE_DTR_SPACE(_Original_GDTR)
309 DECLARE_DTR_SPACE(_New_GDTR)
310
311 DECLARE_SPACE(_Physical_base_of_ds,4,4)
312 DECLARE_SPACE(_Physical_base_of_cs,4,4)
313
314 /*
315  *  Stack Size and Space
316  */
317
318         .set stack_size, 0x20000
319
320 DECLARE_SPACE(stack_memory,stack_size,4)
321 DECLARE_LABEL(stack_end)