Towards making KEEP_IT_REAL work again.
[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_pgh, %ax
103         pushw   %ax
104
105         /* .text16 segment address */
106         subw    $_text16_size_pgh, %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 #ifdef KEEP_IT_REAL
326         /* Preserve %ds, call init_libkir, restore registers */
327         pushw   %ds
328         movw    %bx, %ds
329         movw    %ax, (init_libkir_vector+2)
330         lcall   *init_libkir_vector
331         popw    %ds
332 #else
333         /* Preserve registers and interrupt status, and disable interrupts */
334         pushfw
335         pushw   %ds
336         pushw   %es
337         pushl   %esi
338         pushl   %ecx
339         cli
340
341         /* Load up %ds and %es, and set up vectors for far calls to .text16 */
342         movw    %bx, %ds
343         xorw    %si, %si
344         movw    %si, %es
345         movw    %ax, (init_librm_vector+2)
346         movw    %ax, (prot_call_vector+2)
347         
348         /* Install .text and .data to 2MB mark.  Use 2MB to avoid
349          * problems with A20.
350          */
351         call    flatten_real_mode
352         movl    $(2<<20), %edi
353         call    install_highmem
354
355         /* Set up initial protected-mode GDT, call relocate().
356          * relocate() will return with %esi, %edi and %ecx set up
357          * ready for the copy to the new location.
358          */
359         lcall   *init_librm_vector
360         pushl   $relocate
361         lcall   *prot_call_vector
362         addw    $4, %sp
363
364         /* Move code to new location, set up new protected-mode GDT */
365         call    flatten_real_mode
366         pushl   %edi
367         es rep addr32 movsb
368         popl    %edi
369         lcall   *init_librm_vector
370         
371         /* Hide Etherboot from BIOS memory map.  Note that making this
372          * protected-mode call will also restore normal (non-flat)
373          * real mode, as part of the protected-to-real transition.
374          */
375         pushl   $hide_etherboot
376         lcall   *prot_call_vector
377         addw    $4, %sp
378
379         /* Restore registers and interrupt status */
380         popl    %ecx
381         popl    %esi
382         popw    %es
383         popw    %ds
384         popfw
385 #endif
386         ret
387         .size install_prealloc, . - install_prealloc
388
389         /* Vectors for far calls to .text16 functions */
390         .section ".data16"
391 #ifdef KEEP_IT_REAL
392 init_libkir_vector:
393         .word init_libkir
394         .word 0
395         .size init_libkir_vector, . - init_libkir_vector
396 #else
397 init_librm_vector:
398         .word init_librm
399         .word 0
400         .size init_librm_vector, . - init_librm_vector
401 prot_call_vector:
402         .word prot_call
403         .word 0
404         .size prot_call_vector, . - prot_call_vector
405 #endif