Use a single _payload_offset linker-defined variable to locate the
[people/adir/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 /**
21  * High memory temporary load address
22  *
23  * Temporary buffer into which to copy (or decompress) our runtime
24  * image, prior to calling get_memmap() and relocate().  We don't
25  * actually leave anything here once install() has returned.
26  *
27  * We use the start of an even megabyte so that we don't have to worry
28  * about the current state of the A20 line.
29  *
30  * We use 4MB rather than 2MB because there is at least one commercial
31  * PXE ROM ("Broadcom UNDI, PXE-2.1 (build 082) v2.0.4") which stores
32  * data required by the UNDI ROM loader (yes, the ROM loader; that's
33  * the component which should be impossible to damage short of
34  * screwing with the MMU) around the 2MB mark.  Sadly, this is not a
35  * joke.
36  *
37  */
38 #define HIGHMEM_LOADPOINT ( 4 << 20 )
39
40 #define CR0_PE 1
41
42         .arch i386
43         .section ".prefix.lib", "awx", @progbits
44         .section ".data16", "aw", @progbits
45
46 /****************************************************************************
47  * install_block (real-mode near call)
48  *
49  * Install block to specified address
50  *
51  * Parameters:
52  *   %esi : start offset within loaded image (must be a multiple of 16)
53  *   %es:edi : destination address
54  *   %ecx : length of (decompressed) data
55  *   %edx : total length of block (including any uninitialised data portion)
56  * Returns:
57  *   %esi : end offset within image (rounded up to next multiple of 16)
58  * Corrupts:
59  *   %edi, %ecx, %edx
60  ****************************************************************************
61  */
62         .section ".prefix.lib"
63         .code16
64 install_block:
65         /* Preserve registers */
66         pushw   %ds
67         pushl   %eax
68         pushl   %ebx
69         movl    %esi, %ebx
70         
71         /* Starting segment => %ds */
72         movw    %cs, %ax
73         shrl    $4, %esi
74         addw    %si, %ax
75         movw    %ax, %ds
76         xorl    %esi, %esi
77
78         /* Calculate start and length of uninitialised data portion */
79         addr32 leal     (%edi,%ecx), %eax
80         subl    %ecx, %edx
81         
82         /* Do the copy */
83         cld
84         rep addr32 movsb /* or "call decompress16" */
85
86         /* Zero remaining space */
87         movl    %eax, %edi
88         movl    %edx, %ecx
89         xorb    %al, %al
90         rep addr32 stosb
91
92         /* Adjust %esi */
93         addl    %ebx, %esi
94         addl    $0xf, %esi
95         andl    $~0xf, %esi
96
97         /* Restore registers */
98         popl    %ebx
99         popl    %eax
100         popw    %ds
101         ret
102         .size install_block, . - install_block
103         
104 /****************************************************************************
105  * alloc_basemem (real-mode near call)
106  *
107  * Allocate space for .text16 and .data16 from top of base memory.
108  * Memory is allocated using the BIOS free base memory counter at
109  * 0x40:13.
110  *
111  * Parameters: 
112  *   none
113  * Returns:
114  *   %ax : .text16 segment address
115  *   %bx : .data16 segment address
116  * Corrupts:
117  *   none
118  ****************************************************************************
119  */
120         .section ".prefix.lib"
121         .code16
122 alloc_basemem:
123         /* FBMS => %ax as segment address */
124         movw    $0x40, %ax
125         movw    %ax, %fs
126         movw    %fs:0x13, %ax
127         shlw    $6, %ax
128
129         /* .data16 segment address */
130         subw    $_data16_size_pgh, %ax
131         pushw   %ax
132
133         /* .text16 segment address */
134         subw    $_text16_size_pgh, %ax
135         pushw   %ax
136
137         /* Update FBMS */
138         shrw    $6, %ax
139         movw    %ax, %fs:0x13
140
141         /* Return */
142         popw    %ax
143         popw    %bx
144         ret
145         .size alloc_basemem, . - alloc_basemem
146
147 /****************************************************************************
148  * install_basemem (real-mode near call)
149  *
150  * Install .text16 and .data16 into base memory
151  *
152  * Parameters: 
153  *   %ax : .text16 segment address
154  *   %bx : .data16 segment address
155  *   %esi : start offset within loaded image (must be a multiple of 16)
156  * Returns:
157  *   %esi : end offset within image (rounded up to next multiple of 16)
158  * Corrupts:
159  *   none
160  ****************************************************************************
161  */
162         .section ".prefix.lib"
163         .code16
164 install_basemem:
165         /* Preserve registers */
166         pushw   %es
167         pushl   %edi
168         pushl   %ecx
169         pushl   %edx
170
171         /* Install .text16 */
172         movw    %ax, %es
173         xorl    %edi, %edi
174         movl    $_text16_size, %ecx
175         movl    %ecx, %edx
176         call    install_block
177
178         /* Install .data16 */
179         movw    %bx, %es
180         xorl    %edi, %edi      
181         movl    $_data16_progbits_size, %ecx
182         movl    $_data16_size, %edx
183         call    install_block
184
185         /* Restore registers */
186         popl    %edx
187         popl    %ecx
188         popl    %edi
189         popw    %es
190         ret
191         .size install_basemem, . - install_basemem
192
193 /****************************************************************************
194  * install_highmem (flat real-mode near call)
195  *
196  * Install .text and .data into high memory
197  *
198  * Parameters:
199  *   %esi : start offset within loaded image (must be a multiple of 16)
200  *   %es:edi : address in high memory
201  * Returns:
202  *   %esi : end offset within image (rounded up to next multiple of 16)
203  * Corrupts:
204  *   none
205  ****************************************************************************
206  */
207
208 #ifndef KEEP_IT_REAL
209
210         .section ".prefix.lib"
211         .code16
212 install_highmem:
213         /* Preserve registers */
214         pushl   %edi
215         pushl   %ecx
216         pushl   %edx
217                 
218         /* Install .text and .data to specified address */
219         movl    $_textdata_progbits_size, %ecx
220         movl    $_textdata_size, %edx
221         call    install_block
222
223         /* Restore registers and interrupt status */
224         popl    %edx
225         popl    %ecx
226         popl    %edi
227         ret
228         .size install_highmem, . - install_highmem
229         
230 #endif /* KEEP_IT_REAL */
231         
232 /****************************************************************************
233  * GDT for flat real mode
234  *
235  * We only ever use this GDT to set segment limits; the bases are
236  * unused.  Also, we only change data segments, so we don't need to
237  * worry about the code or stack segments.  This makes everything much
238  * simpler.
239  ****************************************************************************
240  */
241         
242 #ifndef KEEP_IT_REAL
243         
244         .section ".prefix.lib"
245         .align 16
246 gdt:
247 gdt_limit:              .word gdt_length - 1
248 gdt_base:               .long 0
249                         .word 0 /* padding */
250
251 flat_ds:        /* Flat real mode data segment */
252         .equ    FLAT_DS, flat_ds - gdt
253         .word   0xffff, 0
254         .byte   0, 0x93, 0xcf, 0
255
256 real_ds:        /* Normal real mode data segment */
257         .equ    REAL_DS, real_ds - gdt
258         .word   0xffff, 0
259         .byte   0, 0x93, 0x00, 0
260
261 gdt_end:
262         .equ    gdt_length, gdt_end - gdt
263         .size gdt, . - gdt
264         
265 #endif /* KEEP_IT_REAL */
266
267 /****************************************************************************
268  * set_real_mode_limits (real-mode near call)
269  *
270  * Sets limits on the data segments %ds and %es.
271  *
272  * Parameters:
273  *   %cx : segment type (FLAT_DS for 4GB or REAL_DS for 64kB)
274  ****************************************************************************
275  */
276
277 #ifndef KEEP_IT_REAL
278         
279         .section ".prefix.lib"
280         .code16
281 set_real_mode_limits:
282         /* Preserve real-mode segment values and temporary registers */
283         pushw   %es
284         pushw   %ds
285         pushw   %bp
286         pushl   %eax
287
288         /* Set GDT base and load GDT */
289         xorl    %eax, %eax
290         movw    %cs, %ax
291         shll    $4, %eax
292         addl    $gdt, %eax
293         pushl   %eax
294         pushw   %cs:gdt_limit
295         movw    %sp, %bp
296         lgdt    (%bp)
297         addw    $6, %sp
298
299         /* Switch to protected mode */
300         movl    %cr0, %eax
301         orb     $CR0_PE, %al
302         movl    %eax, %cr0
303
304         /* Set flat segment limits */
305         movw    %cx, %ds
306         movw    %cx, %es
307
308         /* Switch back to real mode */
309         movl    %cr0, %eax
310         andb    $0!CR0_PE, %al
311         movl    %eax, %cr0
312
313         /* Restore real-mode segment values and temporary registers */
314         popl    %eax
315         popw    %bp
316         popw    %ds
317         popw    %es
318         ret
319         .size set_real_mode_limits, . - set_real_mode_limits
320         
321 #endif /* KEEP_IT_REAL */
322         
323 /****************************************************************************
324  * install (real-mode near call)
325  * install_prealloc (real-mode near call)
326  *
327  * Install all text and data segments.
328  *
329  * Parameters:
330  *   %ax : .text16 segment address (install_prealloc only)
331  *   %bx : .data16 segment address (install_prealloc only)
332  * Returns:
333  *   %ax : .text16 segment address
334  *   %bx : .data16 segment address
335  *   %edi : .text physical address (if applicable)
336  * Corrupts:
337  *   none
338  ****************************************************************************
339  */
340         .section ".prefix.lib"
341         .code16
342         .globl install
343 install:
344         /* Allocate space for .text16 and .data16 */
345         call    alloc_basemem
346         .size install, . - install
347         .globl install_prealloc
348 install_prealloc:
349         /* Save registers */
350         pushl   %esi
351         /* Install .text16 and .data16 */
352         movl    $_payload_offset, %esi
353         call    install_basemem
354
355 #ifdef KEEP_IT_REAL
356         /* Preserve %ds, call init_libkir, restore registers */
357         pushw   %ds
358         movw    %bx, %ds
359         movw    %ax, (init_libkir_vector+2)
360         lcall   *init_libkir_vector
361         popw    %ds
362 #else
363         /* Preserve registers and interrupt status, and disable interrupts */
364         pushfw
365         pushw   %ds
366         pushw   %es
367         pushl   %ecx
368         cli
369
370         /* Load up %ds and %es, and set up vectors for far calls to .text16 */
371         movw    %bx, %ds
372         xorw    %cx, %cx
373         movw    %cx, %es
374         movw    %ax, (init_librm_vector+2)
375         movw    %ax, (prot_call_vector+2)
376         
377         /* Install .text and .data to temporary area in high memory,
378          * prior to reading the E820 memory map and relocating
379          * properly.
380          */
381         movw    $FLAT_DS, %cx
382         call    set_real_mode_limits
383         movl    $HIGHMEM_LOADPOINT, %edi
384         call    install_highmem
385
386         /* Set up initial protected-mode GDT, call relocate().
387          * relocate() will return with %esi, %edi and %ecx set up
388          * ready for the copy to the new location.
389          */
390         lcall   *init_librm_vector
391         pushl   $relocate
392         lcall   *prot_call_vector
393         addw    $4, %sp
394
395         /* Move code to new location, set up new protected-mode GDT */
396         movw    $FLAT_DS, %cx
397         call    set_real_mode_limits
398         pushl   %edi
399         es rep addr32 movsb
400         popl    %edi
401         lcall   *init_librm_vector
402
403         /* Restore real-mode segment limits */
404         movw    $REAL_DS, %cx
405         call    set_real_mode_limits
406
407         /* Restore registers and interrupt status */
408         popl    %ecx
409         popw    %es
410         popw    %ds
411         popfw
412 #endif
413         popl    %esi
414         ret
415         .size install_prealloc, . - install_prealloc
416
417         /* Vectors for far calls to .text16 functions */
418         .section ".data16"
419 #ifdef KEEP_IT_REAL
420 init_libkir_vector:
421         .word init_libkir
422         .word 0
423         .size init_libkir_vector, . - init_libkir_vector
424 #else
425 init_librm_vector:
426         .word init_librm
427         .word 0
428         .size init_librm_vector, . - init_librm_vector
429 prot_call_vector:
430         .word prot_call
431         .word 0
432         .size prot_call_vector, . - prot_call_vector
433 #endif