406dac3930226000487e4b82c63b9db6b28f0a1b
[people/mdeck/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         .arch i386
21         .section ".prefix.lib", "awx", @progbits
22         .section ".data16", "aw", @progbits
23
24 /**
25  * High memory temporary load address
26  *
27  * Temporary buffer into which to copy (or decompress) our runtime
28  * image, prior to calling get_memmap() and relocate().  We don't
29  * actually leave anything here once install() has returned.
30  *
31  * We use the start of an even megabyte so that we don't have to worry
32  * about the current state of the A20 line.
33  *
34  * We use 4MB rather than 2MB because some PXE stack / PMM BIOS
35  * combinations are known to place data required by other UNDI ROMs
36  * loader around the 2MB mark.
37  */
38         .globl  HIGHMEM_LOADPOINT
39         .equ    HIGHMEM_LOADPOINT, ( 4 << 20 )
40
41 /* Image compression enabled */
42 #define COMPRESS 1
43
44 #define CR0_PE 1
45
46 /****************************************************************************
47  * pm_call (real-mode near call)
48  *
49  * Call routine in 16-bit protected mode for access to extended memory
50  *
51  * Parameters:
52  *   %ax : address of routine to call in 16-bit protected mode
53  * Returns:
54  *   none
55  * Corrupts:
56  *   %ax
57  *
58  * The specified routine is called in 16-bit protected mode, with:
59  *
60  *   %cs : 16-bit code segment with base matching real-mode %cs
61  *   %ss : 16-bit data segment with base matching real-mode %ss
62  *   %ds,%es,%fs,%gs : 32-bit data segment with zero base and 4GB limit
63  *
64  ****************************************************************************
65  */
66
67 #ifndef KEEP_IT_REAL
68
69         /* GDT for protected-mode calls */
70         .section ".data16"
71         .align 16
72 gdt:
73 gdt_limit:              .word gdt_length - 1
74 gdt_base:               .long 0
75                         .word 0 /* padding */
76 pm_cs:          /* 16-bit protected-mode code segment */
77         .equ    PM_CS, pm_cs - gdt
78         .word   0xffff, 0
79         .byte   0, 0x9b, 0x00, 0
80 pm_ss:          /* 16-bit protected-mode stack segment */
81         .equ    PM_SS, pm_ss - gdt
82         .word   0xffff, 0
83         .byte   0, 0x93, 0x00, 0
84 pm_ds:          /* 32-bit protected-mode flat data segment */
85         .equ    PM_DS, pm_ds - gdt
86         .word   0xffff, 0
87         .byte   0, 0x93, 0xcf, 0
88 gdt_end:
89         .equ    gdt_length, . - gdt
90         .size   gdt, . - gdt
91
92         .section ".data16"
93         .align 16
94 pm_saved_gdt:   
95         .long   0, 0
96         .size   pm_saved_gdt, . - pm_saved_gdt
97
98         .section ".prefix.lib"
99         .code16
100 pm_call:
101         /* Preserve registers, flags, GDT, and RM return point */
102         pushfl
103         sgdt    pm_saved_gdt
104         pushw   %gs
105         pushw   %fs
106         pushw   %es
107         pushw   %ds
108         pushw   %ss
109         pushw   %cs
110         pushw   $99f
111
112         /* Set up GDT bases */
113         pushl   %eax
114         pushw   %bx
115         xorl    %eax, %eax
116         movw    %ds, %ax
117         shll    $4, %eax
118         addl    $gdt, %eax
119         movl    %eax, gdt_base
120         movw    %cs, %ax
121         movw    $pm_cs, %bx
122         call    set_seg_base
123         movw    %ss, %ax
124         movw    $pm_ss, %bx
125         call    set_seg_base
126         popw    %bx
127         popl    %eax
128
129         /* Switch CPU to protected mode and load up segment registers */
130         pushl   %eax
131         cli
132         lgdt    gdt
133         movl    %cr0, %eax
134         orb     $CR0_PE, %al
135         movl    %eax, %cr0
136         ljmp    $PM_CS, $1f
137 1:      movw    $PM_SS, %ax
138         movw    %ax, %ss
139         movw    $PM_DS, %ax
140         movw    %ax, %ds
141         movw    %ax, %es
142         movw    %ax, %fs
143         movw    %ax, %gs
144         popl    %eax
145
146         /* Call PM routine */
147         call    *%ax
148
149         /* Set real-mode segment limits on %ds, %es, %fs and %gs */
150         movw    %ss, %ax
151         movw    %ax, %ds
152         movw    %ax, %es
153         movw    %ax, %fs
154         movw    %ax, %gs
155
156         /* Return CPU to real mode */
157         movl    %cr0, %eax
158         andb    $0!CR0_PE, %al
159         movl    %eax, %cr0
160
161         /* Restore registers and flags */
162         lret    /* will ljmp to 99f */
163 99:     popw    %ss
164         popw    %ds
165         popw    %es
166         popw    %fs
167         popw    %gs
168         lgdt    pm_saved_gdt
169         popfl
170
171         ret
172         .size pm_call, . - pm_call
173
174 set_seg_base:
175         rolw    $4, %ax
176         movw    %ax, 2(%bx)
177         andw    $0xfff0, 2(%bx)
178         movb    %al, 4(%bx)
179         andb    $0x0f, 4(%bx)
180         ret
181         .size set_seg_base, . - set_seg_base
182
183 #endif /* KEEP_IT_REAL */
184
185 /****************************************************************************
186  * copy_bytes (real-mode or 16-bit protected-mode near call)
187  *
188  * Copy bytes
189  *
190  * Parameters:
191  *   %ds:esi : source address
192  *   %es:edi : destination address
193  *   %ecx : length
194  * Returns:
195  *   %ds:esi : next source address
196  *   %ds:esi : next destination address
197  * Corrupts:
198  *   None
199  ****************************************************************************
200  */
201         .section ".prefix.lib"
202         .code16
203 copy_bytes:
204         pushl %ecx
205         rep addr32 movsb
206         popl %ecx
207         ret
208         .size copy_bytes, . - copy_bytes
209
210 /****************************************************************************
211  * install_block (real-mode or 16-bit protected-mode near call)
212  *
213  * Install block to specified address
214  *
215  * Parameters:
216  *   %ds:esi : source address (must be a multiple of 16)
217  *   %es:edi : destination address
218  *   %ecx : length of (decompressed) data
219  *   %edx : total length of block (including any uninitialised data portion)
220  * Returns:
221  *   %ds:esi : next source address (will be a multiple of 16)
222  * Corrupts:
223  *   %ecx, %edx
224  ****************************************************************************
225  */
226         .section ".prefix.lib"
227         .code16
228 install_block:
229         /* Preserve registers */
230         pushl   %edi
231         
232 #if COMPRESS
233         /* Decompress source to destination */
234         call    decompress16
235 #else
236         /* Copy source to destination */
237         call    copy_bytes
238 #endif
239
240         /* Zero .bss portion */
241         negl    %ecx
242         addl    %edx, %ecx
243         pushw   %ax
244         xorw    %ax, %ax
245         rep addr32 stosb
246         popw    %ax
247
248         /* Round up %esi to start of next source block */
249         addl    $0xf, %esi
250         andl    $~0xf, %esi
251
252         /* Restore registers and return */
253         popl    %edi
254         ret
255         .size install_block, . - install_block
256         
257 /****************************************************************************
258  * alloc_basemem (real-mode near call)
259  *
260  * Allocate space for .text16 and .data16 from top of base memory.
261  * Memory is allocated using the BIOS free base memory counter at
262  * 0x40:13.
263  *
264  * Parameters: 
265  *   none
266  * Returns:
267  *   %ax : .text16 segment address
268  *   %bx : .data16 segment address
269  * Corrupts:
270  *   none
271  ****************************************************************************
272  */
273         .section ".prefix.lib"
274         .code16
275         .globl  alloc_basemem
276 alloc_basemem:
277         /* FBMS => %ax as segment address */
278         movw    $0x40, %ax
279         movw    %ax, %fs
280         movw    %fs:0x13, %ax
281         shlw    $6, %ax
282
283         /* .data16 segment address */
284         subw    $_data16_size_pgh, %ax
285         pushw   %ax
286
287         /* .text16 segment address */
288         subw    $_text16_size_pgh, %ax
289         pushw   %ax
290
291         /* Update FBMS */
292         shrw    $6, %ax
293         movw    %ax, %fs:0x13
294
295         /* Return */
296         popw    %ax
297         popw    %bx
298         ret
299         .size alloc_basemem, . - alloc_basemem
300
301 /****************************************************************************
302  * install_basemem (real-mode near call)
303  *
304  * Install source block into base memory
305  *
306  * Parameters:
307  *   %esi : source physical address (must be a multiple of 16)
308  *   %es : destination segment address
309  *   %cx : length of (decompressed) data
310  *   %dx : total length of block (including any uninitialised data portion)
311  * Returns:
312  *   %esi : next source physical address (will be a multiple of 16)
313  * Corrupts:
314  *   %ecx, %edx
315  ****************************************************************************
316  */
317         .section ".prefix.lib"
318         .code16
319 install_basemem:
320         /* Preserve registers */
321         pushl   %edi
322         pushw   %ds
323
324         /* Preserve original %esi */
325         pushl   %esi
326
327         /* Install to specified address */
328         shrl    $4, %esi
329         movw    %si, %ds
330         xorw    %si, %si
331         xorl    %edi, %edi
332         movzwl  %cx, %ecx
333         movzwl  %dx, %edx
334         call    install_block
335
336         /* Fix up %esi for return */
337         popl    %ecx
338         addl    %ecx, %esi
339
340         /* Restore registers */
341         popw    %ds
342         popl    %edi
343         ret
344         .size install_basemem, . - install_basemem
345
346 /****************************************************************************
347  * install_highmem (real-mode near call)
348  *
349  * Install source block into high memory
350  *
351  * Parameters:
352  *   %esi : source physical address (must be a multiple of 16)
353  *   %edi : destination physical address
354  *   %ecx : length of (decompressed) data
355  *   %edx : total length of block (including any uninitialised data portion)
356  * Returns:
357  *   %esi : next source physical address (will be a multiple of 16)
358  * Corrupts:
359  *   %ecx, %edx
360  ****************************************************************************
361  */
362
363 #ifndef KEEP_IT_REAL
364
365         .section ".prefix.lib"
366         .code16
367 install_highmem:
368         /* Preserve registers */
369         pushw   %ax
370
371         /* Install to specified address */
372         movw    $install_block, %ax
373         call    pm_call
374
375         /* Restore registers */
376         popw    %ax
377         ret
378         .size install_highmem, . - install_highmem
379         
380 #endif /* KEEP_IT_REAL */
381         
382 /****************************************************************************
383  * install (real-mode near call)
384  *
385  * Install all text and data segments.
386  *
387  * Parameters:
388  *   none
389  * Returns:
390  *   %ax  : .text16 segment address
391  *   %bx  : .data16 segment address
392  * Corrupts:
393  *   none
394  ****************************************************************************
395  */
396         .section ".prefix.lib"
397         .code16
398         .globl install
399 install:
400         /* Preserve registers */
401         pushl   %esi
402         pushl   %edi
403         /* Allocate space for .text16 and .data16 */
404         call    alloc_basemem
405         /* Image source = %cs:0000 */
406         xorl    %esi, %esi
407         /* Image destination = HIGHMEM_LOADPOINT */
408         movl    $HIGHMEM_LOADPOINT, %edi
409         /* Install text and data segments */
410         call    install_prealloc
411         /* Restore registers and return */
412         popl    %edi
413         popl    %esi
414         ret
415         .size install, . - install
416
417 /****************************************************************************
418  * install_prealloc (real-mode near call)
419  *
420  * Install all text and data segments.
421  *
422  * Parameters:
423  *   %ax  : .text16 segment address
424  *   %bx  : .data16 segment address
425  *   %esi : Image source physical address (or zero for %cs:0000)
426  *   %edi : Decompression temporary area physical address
427  * Corrupts:
428  *   none
429  ****************************************************************************
430  */
431         .section ".prefix.lib"
432         .code16
433         .globl install_prealloc
434 install_prealloc:
435         /* Save registers */
436         pushal
437         pushw   %ds
438         pushw   %es
439
440         /* Sanity: clear the direction flag asap */
441         cld
442
443         /* Calculate physical address of payload (i.e. first source) */
444         testl   %esi, %esi
445         jnz     1f
446         movw    %cs, %si
447         shll    $4, %esi
448 1:      addl    $_payload_offset, %esi
449
450         /* Install .text16 */
451         movw    %ax, %es
452         movw    $_text16_size, %cx
453         movw    %cx, %dx
454         call    install_basemem
455
456         /* Install .data16 */
457         movw    %bx, %es
458         movw    $_data16_progbits_size, %cx
459         movw    $_data16_size, %dx
460         call    install_basemem
461
462         /* Set up %ds for access to .data16 */
463         movw    %bx, %ds
464
465 #ifdef KEEP_IT_REAL
466         /* Initialise libkir */
467         movw    %ax, (init_libkir_vector+2)
468         lcall   *init_libkir_vector
469 #else
470         /* Install .text and .data to temporary area in high memory,
471          * prior to reading the E820 memory map and relocating
472          * properly.
473          */
474         movl    $_textdata_progbits_size, %ecx
475         movl    $_textdata_size, %edx
476         pushl   %edi
477         call    install_highmem
478         popl    %edi
479
480         /* Initialise librm at current location */
481         movw    %ax, (init_librm_vector+2)
482         lcall   *init_librm_vector
483
484         /* Call relocate() to determine target address for relocation.
485          * relocate() will return with %esi, %edi and %ecx set up
486          * ready for the copy to the new location.
487          */
488         movw    %ax, (prot_call_vector+2)
489         pushl   $relocate
490         lcall   *prot_call_vector
491         popl    %edx /* discard */
492
493         /* Copy code to new location */
494         pushl   %edi
495         pushw   %ax
496         movw    $copy_bytes, %ax
497         call    pm_call
498         popw    %ax
499         popl    %edi
500
501         /* Initialise librm at new location */
502         lcall   *init_librm_vector
503
504 #endif
505         /* Restore registers */
506         popw    %es
507         popw    %ds
508         popal
509         ret
510         .size install_prealloc, . - install_prealloc
511
512         /* Vectors for far calls to .text16 functions */
513         .section ".data16"
514 #ifdef KEEP_IT_REAL
515 init_libkir_vector:
516         .word init_libkir
517         .word 0
518         .size init_libkir_vector, . - init_libkir_vector
519 #else
520 init_librm_vector:
521         .word init_librm
522         .word 0
523         .size init_librm_vector, . - init_librm_vector
524 prot_call_vector:
525         .word prot_call
526         .word 0
527         .size prot_call_vector, . - prot_call_vector
528 #endif
529
530
531         /* File split information for the compressor */
532 #if COMPRESS
533         .section ".zinfo", "a"
534         .ascii  "COPY"
535         .long   _prefix_load_offset
536         .long   _prefix_progbits_size
537         .long   _max_align
538         .ascii  "PACK"
539         .long   _text16_load_offset
540         .long   _text16_progbits_size
541         .long   _max_align
542         .ascii  "PACK"
543         .long   _data16_load_offset
544         .long   _data16_progbits_size
545         .long   _max_align
546         .ascii  "PACK"
547         .long   _textdata_load_offset
548         .long   _textdata_progbits_size
549         .long   _max_align
550 #else /* COMPRESS */
551         .section ".zinfo", "a"
552         .ascii  "COPY"
553         .long   _prefix_load_offset
554         .long   _load_size
555         .long   _max_align
556 #endif /* COMPRESS */