f0f7682b5ed2e3960f772eaee08d447366ade508
[people/lynusvaz/gpxe.git] / src / arch / i386 / prefix / libprefix.S
1 /*
2  * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  */
19
20 #define CR0_PE 1
21
22         .arch i386
23         .section ".prefix.lib", "awx", @progbits
24         .section ".data16", "aw", @progbits
25
26 /****************************************************************************
27  * install_block (real-mode near call)
28  *
29  * Install block to specified address
30  *
31  * Parameters:
32  *   %esi : byte offset within loaded image (must be a multiple of 16)
33  *   %es:edi : destination address
34  *   %ecx : length of (decompressed) data
35  *   %edx : total length of block (including any uninitialised data portion)
36  * Returns:
37  *   none
38  * Corrupts:
39  *   %esi, %edi, %ecx, %edx
40  ****************************************************************************
41  */
42         .section ".prefix.lib"
43         .code16
44 install_block:
45         /* Preserve registers */
46         pushw   %ds
47         pushl   %eax
48         
49         /* Starting segment => %ds */
50         movw    %cs, %ax
51         shrl    $4, %esi
52         addw    %si, %ax
53         movw    %ax, %ds
54         xorl    %esi, %esi
55
56         /* Calculate start and length of uninitialised data portion */
57         addr32 leal     (%edi,%ecx), %eax
58         subl    %ecx, %edx
59         
60         /* Do the copy */
61         cld
62         rep addr32 movsb /* or "call decompress16" */
63
64         /* Zero remaining space */
65         movl    %eax, %edi
66         movl    %edx, %ecx
67         xorb    %al, %al
68         rep addr32 stosb
69
70         /* Restore registers */
71         popl    %eax
72         popw    %ds
73         ret
74         .size install_block, . - install_block
75         
76 /****************************************************************************
77  * alloc_basemem (real-mode near call)
78  *
79  * Allocate space for .text16 and .data16 from top of base memory.
80  * Memory is allocated using the BIOS free base memory counter at
81  * 0x40:13.
82  *
83  * Parameters: 
84  *   none
85  * Returns:
86  *   %ax : .text16 segment address
87  *   %bx : .data16 segment address
88  * Corrupts:
89  *   none
90  ****************************************************************************
91  */
92         .section ".prefix.lib"
93         .code16
94 alloc_basemem:
95         /* FBMS => %ax as segment address */
96         movw    $0x40, %ax
97         movw    %ax, %fs
98         movw    %fs:0x13, %ax
99         shlw    $6, %ax
100
101         /* .data16 segment address */
102         subw    $_data16_size, %ax
103         pushw   %ax
104
105         /* .text16 segment address */
106         subw    $_text16_size, %ax
107         pushw   %ax
108
109         /* Update FBMS */
110         shrw    $6, %ax
111         movw    %ax, %fs:0x13
112
113         /* Return */
114         popw    %ax
115         popw    %bx
116         ret
117         .size alloc_basemem, . - alloc_basemem
118
119 /****************************************************************************
120  * install_basemem (real-mode near call)
121  *
122  * Install .text16 and .data16 into base memory
123  *
124  * Parameters: 
125  *   %ax : .text16 segment address
126  *   %bx : .data16 segment address
127  * Returns:
128  *   none
129  * Corrupts:
130  *   none
131  ****************************************************************************
132  */
133         .section ".prefix.lib"
134         .code16
135 install_basemem:
136         /* Preserve registers */
137         pushw   %es
138         pushl   %esi
139         pushl   %edi
140         pushl   %ecx
141         pushl   %edx
142
143         /* Install .text16 */
144         movw    %ax, %es
145         xorl    %edi, %edi
146         movl    $_text16_load_offset, %esi
147         movl    $_text16_size, %ecx
148         movl    %ecx, %edx
149         call    install_block
150
151         /* Install .data16 */
152         movw    %bx, %es
153         xorl    %edi, %edi      
154         movl    $_data16_load_offset, %esi
155         movl    $_data16_progbits_size, %ecx
156         movl    $_data16_size, %edx
157         call    install_block
158
159         /* Restore registers */
160         popl    %edx
161         popl    %ecx
162         popl    %edi
163         popl    %esi
164         popw    %es
165         ret
166         .size install_basemem, . - install_basemem
167
168 /****************************************************************************
169  * install_highmem (flat real-mode near call)
170  *
171  * Install .text and .data into high memory
172  *
173  * Parameters:
174  *   %es:edi : address in high memory
175  * Returns:
176  *   none
177  * Corrupts:
178  *   none
179  ****************************************************************************
180  */
181
182 #ifndef KEEP_IT_REAL
183
184         .section ".prefix.lib"
185         .code16
186 install_highmem:
187         /* Preserve registers */
188         pushl   %esi
189         pushl   %edi
190         pushl   %ecx
191         pushl   %edx
192                 
193         /* Install .text and .data to specified address */
194         movl    $_textdata_load_offset, %esi
195         movl    $_textdata_progbits_size, %ecx
196         movl    $_textdata_size, %edx
197         call    install_block
198
199         /* Restore registers and interrupt status */
200         popl    %edx
201         popl    %ecx
202         popl    %edi
203         popl    %esi
204         ret
205         .size install_highmem, . - install_highmem
206         
207 #endif /* KEEP_IT_REAL */
208         
209 /****************************************************************************
210  * GDT for flat real mode
211  *
212  * We only ever use this GDT to set segment limits; the bases are
213  * unused.  Also, we only flatten data segments, so we don't need to
214  * worry about the code or stack segments.  This makes everything much
215  * simpler.
216  ****************************************************************************
217  */
218         
219 #ifndef KEEP_IT_REAL
220         
221         .section ".prefix.lib"
222         .align 16
223 gdt:
224 gdt_limit:              .word gdt_length - 1
225 gdt_base:               .long 0
226                         .word 0 /* padding */
227
228 flat_ds:        /* Flat real mode data segment */
229         .equ    FLAT_DS, flat_ds - gdt
230         .word   0xffff, 0
231         .byte   0, 0x93, 0xcf, 0
232
233 gdt_end:
234         .equ    gdt_length, gdt_end - gdt
235         .size gdt, . - gdt
236         
237 #endif /* KEEP_IT_REAL */
238
239 /****************************************************************************
240  * flatten_real_mode (real-mode near call)
241  *
242  * Sets 4GB limits on the data segments %ds and %es.
243  *
244  * Parameters: 
245  *   none
246  ****************************************************************************
247  */
248
249 #ifndef KEEP_IT_REAL
250         
251         .section ".prefix.lib"
252         .code16
253 flatten_real_mode:
254         /* Preserve real-mode segment values and temporary registers */
255         pushw   %es
256         pushw   %ds
257         pushw   %bp
258         pushl   %eax
259
260         /* Set GDT base and load GDT */
261         xorl    %eax, %eax
262         movw    %cs, %ax
263         shll    $4, %eax
264         addl    $gdt, %eax
265         pushl   %eax
266         pushw   %cs:gdt_limit
267         movw    %sp, %bp
268         lgdt    (%bp)
269         addw    $6, %sp
270
271         /* Switch to protected mode */
272         movl    %cr0, %eax
273         orb     $CR0_PE, %al
274         movl    %eax, %cr0
275
276         /* Set flat segment limits */
277         movw    $FLAT_DS, %ax
278         movw    %ax, %ds
279         movw    %ax, %es
280
281         /* Switch back to real mode */
282         movl    %cr0, %eax
283         andb    $0!CR0_PE, %al
284         movl    %eax, %cr0
285
286         /* Restore real-mode segment values and temporary registers */
287         popl    %eax
288         popw    %bp
289         popw    %ds
290         popw    %es
291         ret
292         .size flatten_real_mode, . - flatten_real_mode
293         
294 #endif /* KEEP_IT_REAL */
295         
296 /****************************************************************************
297  * install (real-mode near call)
298  * install_prealloc (real-mode near call)
299  *
300  * Install all text and data segments.
301  *
302  * Parameters:
303  *   %ax : .text16 segment address (install_prealloc only)
304  *   %bx : .data16 segment address (install_prealloc only)
305  * Returns:
306  *   %ax : .text16 segment address
307  *   %bx : .data16 segment address
308  *   %edi : .text physical address (if applicable)
309  * Corrupts:
310  *   none
311  ****************************************************************************
312  */
313         .section ".prefix.lib"
314         .code16
315         .globl install
316 install:
317         /* Allocate space for .text16 and .data16 */
318         call    alloc_basemem
319         .size install, . - install
320         .globl install_prealloc
321 install_prealloc:
322         /* Install .text16 and .data16 */
323         call    install_basemem
324
325 #ifndef KEEP_IT_REAL
326         /* Preserve registers and interrupt status, and disable interrupts */
327         pushfw
328         pushw   %ds
329         pushw   %es
330         pushl   %esi
331         pushl   %ecx
332         cli
333
334         /* Load up %ds and %es, and set up vectors for far calls to .text16 */
335         movw    %bx, %ds
336         xorw    %si, %si
337         movw    %si, %es
338         movw    %ax, (init_librm_vector+2)
339         movw    %ax, (prot_call_vector+2)
340         
341         /* Install .text and .data to 2MB mark.  Use 2MB to avoid
342          * problems with A20.
343          */
344         call    flatten_real_mode
345         movl    $(2<<20), %edi
346         call    install_highmem
347
348         /* Set up initial protected-mode GDT, call relocate().
349          * relocate() will return with %esi, %edi and %ecx set up
350          * ready for the copy to the new location.
351          */
352         lcall   *init_librm_vector
353         pushl   $relocate
354         lcall   *prot_call_vector
355         addw    $4, %sp
356
357         /* Move code to new location, set up new protected-mode GDT */
358         call    flatten_real_mode
359         pushl   %edi
360         es rep addr32 movsb
361         popl    %edi
362         lcall   *init_librm_vector
363         
364         /* Hide Etherboot from BIOS memory map.  Note that making this
365          * protected-mode call will also restore normal (non-flat)
366          * real mode, as part of the protected-to-real transition.
367          */
368         pushl   $hide_etherboot
369         lcall   *prot_call_vector
370         addw    $4, %sp
371
372         /* Restore registers and interrupt status */
373         popl    %ecx
374         popl    %esi
375         popw    %es
376         popw    %ds
377         popfw
378 #endif
379         ret
380         .size install_prealloc, . - install_prealloc
381
382 #ifndef KEEP_IT_REAL
383         /* Vectors for far calls to .text16 functions */
384         .section ".data16"
385 init_librm_vector:
386         .word init_librm
387         .word 0
388         .size init_librm_vector, . - init_librm_vector
389 prot_call_vector:
390         .word prot_call
391         .word 0
392         .size prot_call_vector, . - prot_call_vector
393 #endif