deea5ab30c2fab22ff16c624c29c448f0859b5fc
[people/dverkamp/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  * Utility function: print character (with LF -> LF,CR translation)
48  *
49  * Parameters:
50  *   %al : character to print
51  * Returns:
52  *   Nothing
53  * Corrupts:
54  *   %ax
55  *****************************************************************************
56  */
57         .section ".prefix.lib"
58         .code16
59         .globl  print_character
60 print_character:
61         /* Preserve registers */
62         pushw   %bx
63         pushw   %bp
64         /* Print character */
65         movw    $0x0007, %bx            /* page 0, attribute 7 (normal) */
66         movb    $0x0e, %ah              /* write char, tty mode */
67         cmpb    $0x0a, %al              /* '\n'? */
68         jne     1f
69         int     $0x10
70         movb    $0x0d, %al
71 1:      int     $0x10
72         /* Restore registers and return */
73         popw    %bp
74         popw    %bx
75         ret
76         .size   print_character, . - print_character
77
78 /*****************************************************************************
79  * Utility function: print a NUL-terminated string
80  *
81  * Parameters:
82  *   %ds:si : string to print
83  * Returns:
84  *   %ds:si : character after terminating NUL
85  *****************************************************************************
86  */
87         .section ".prefix.lib"
88         .code16
89         .globl  print_message
90 print_message:
91         /* Preserve registers */
92         pushw   %ax
93         /* Print string */
94 1:      lodsb
95         testb   %al, %al
96         je      2f
97         call    print_character
98         jmp     1b
99 2:      /* Restore registers and return */
100         popw    %ax
101         ret
102         .size   print_message, . - print_message
103
104 /*****************************************************************************
105  * Utility functions: print hex digit/byte/word/dword
106  *
107  * Parameters:
108  *   %al (low nibble) : digit to print
109  *   %al : byte to print
110  *   %ax : word to print
111  *   %eax : dword to print
112  * Returns:
113  *   Nothing
114  *****************************************************************************
115  */
116         .section ".prefix.lib"
117         .code16
118         .globl  print_hex_dword
119 print_hex_dword:
120         rorl    $16, %eax
121         call    print_hex_word
122         rorl    $16, %eax
123         /* Fall through */
124         .size   print_hex_dword, . - print_hex_dword
125         .globl  print_hex_word
126 print_hex_word:
127         xchgb   %al, %ah
128         call    print_hex_byte
129         xchgb   %al, %ah
130         /* Fall through */
131         .size   print_hex_word, . - print_hex_word
132         .globl  print_hex_byte
133 print_hex_byte:
134         rorb    $4, %al
135         call    print_hex_nibble
136         rorb    $4, %al
137         /* Fall through */
138         .size   print_hex_byte, . - print_hex_byte
139         .globl  print_hex_nibble
140 print_hex_nibble:
141         /* Preserve registers */
142         pushw   %ax
143         /* Print digit (technique by Norbert Juffa <norbert.juffa@amd.com> */
144         andb    $0x0f, %al
145         cmpb    $10, %al
146         sbbb    $0x69, %al
147         das
148         call    print_character
149         /* Restore registers and return */
150         popw    %ax
151         ret
152         .size   print_hex_nibble, . - print_hex_nibble
153
154 /****************************************************************************
155  * pm_call (real-mode near call)
156  *
157  * Call routine in 16-bit protected mode for access to extended memory
158  *
159  * Parameters:
160  *   %ax : address of routine to call in 16-bit protected mode
161  * Returns:
162  *   none
163  * Corrupts:
164  *   %ax
165  *
166  * The specified routine is called in 16-bit protected mode, with:
167  *
168  *   %cs : 16-bit code segment with base matching real-mode %cs
169  *   %ss : 16-bit data segment with base matching real-mode %ss
170  *   %ds,%es,%fs,%gs : 32-bit data segment with zero base and 4GB limit
171  *
172  ****************************************************************************
173  */
174
175 #ifndef KEEP_IT_REAL
176
177         /* GDT for protected-mode calls */
178         .section ".prefix.lib"
179         .align 16
180 pm_call_vars:
181 gdt:
182 gdt_limit:              .word gdt_length - 1
183 gdt_base:               .long 0
184                         .word 0 /* padding */
185 pm_cs:          /* 16-bit protected-mode code segment */        
186         .equ    PM_CS, pm_cs - gdt
187         .word   0xffff, 0
188         .byte   0, 0x9b, 0x00, 0
189 pm_ss:          /* 16-bit protected-mode stack segment */
190         .equ    PM_SS, pm_ss - gdt
191         .word   0xffff, 0
192         .byte   0, 0x93, 0x00, 0
193 pm_ds:          /* 32-bit protected-mode flat data segment */
194         .equ    PM_DS, pm_ds - gdt
195         .word   0xffff, 0
196         .byte   0, 0x93, 0xcf, 0
197 gdt_end:
198         .equ    gdt_length, . - gdt
199         .size   gdt, . - gdt
200
201         .section ".prefix.lib"
202         .align 16
203 pm_saved_gdt:   
204         .long   0, 0
205         .size   pm_saved_gdt, . - pm_saved_gdt
206
207         .equ    pm_call_vars_size, . - pm_call_vars
208 #define PM_CALL_VAR(x) ( -pm_call_vars_size + ( (x) - pm_call_vars ) )
209
210         .section ".prefix.lib"
211         .code16
212 pm_call:
213         /* Preserve registers, flags, and RM return point */
214         pushw   %bp
215         movw    %sp, %bp
216         subw    $pm_call_vars_size, %sp
217         andw    $0xfff0, %sp
218         pushfl
219         pushw   %gs
220         pushw   %fs
221         pushw   %es
222         pushw   %ds
223         pushw   %ss
224         pushw   %cs
225         pushw   $99f
226
227         /* Set up local variable block, and preserve GDT */
228         pushw   %cx
229         pushw   %si
230         pushw   %di
231         pushw   %ss
232         popw    %es
233         movw    $pm_call_vars, %si
234         leaw    PM_CALL_VAR(pm_call_vars)(%bp), %di
235         movw    $pm_call_vars_size, %cx
236         cs rep movsb
237         popw    %di
238         popw    %si
239         popw    %cx
240         sgdt    PM_CALL_VAR(pm_saved_gdt)(%bp)
241
242         /* Set up GDT bases */
243         pushl   %eax
244         pushl   %edi
245         xorl    %eax, %eax
246         movw    %ss, %ax
247         shll    $4, %eax
248         movzwl  %bp, %edi
249         leal    PM_CALL_VAR(gdt)(%eax, %edi), %eax
250         movl    %eax, PM_CALL_VAR(gdt_base)(%bp)
251         movw    %cs, %ax
252         movw    $PM_CALL_VAR(pm_cs), %di
253         call    set_seg_base
254         movw    %ss, %ax
255         movw    $PM_CALL_VAR(pm_ss), %di
256         call    set_seg_base
257         popl    %edi
258         popl    %eax
259
260         /* Switch CPU to protected mode and load up segment registers */
261         pushl   %eax
262         cli
263         lgdt    PM_CALL_VAR(gdt)(%bp)
264         movl    %cr0, %eax
265         orb     $CR0_PE, %al
266         movl    %eax, %cr0
267         ljmp    $PM_CS, $1f
268 1:      movw    $PM_SS, %ax
269         movw    %ax, %ss
270         movw    $PM_DS, %ax
271         movw    %ax, %ds
272         movw    %ax, %es
273         movw    %ax, %fs
274         movw    %ax, %gs
275         popl    %eax
276
277         /* Call PM routine */
278         call    *%ax
279
280         /* Set real-mode segment limits on %ds, %es, %fs and %gs */
281         movw    %ss, %ax
282         movw    %ax, %ds
283         movw    %ax, %es
284         movw    %ax, %fs
285         movw    %ax, %gs
286
287         /* Return CPU to real mode */
288         movl    %cr0, %eax
289         andb    $0!CR0_PE, %al
290         movl    %eax, %cr0
291
292         /* Restore registers and flags */
293         lret    /* will ljmp to 99f */
294 99:     popw    %ss
295         popw    %ds
296         popw    %es
297         popw    %fs
298         popw    %gs
299         lgdt    PM_CALL_VAR(pm_saved_gdt)(%bp)
300         popfl
301         movw    %bp, %sp
302         popw    %bp
303         ret
304         .size pm_call, . - pm_call
305
306 set_seg_base:
307         rolw    $4, %ax
308         movw    %ax, 2(%bp,%di)
309         andw    $0xfff0, 2(%bp,%di)
310         movb    %al, 4(%bp,%di)
311         andb    $0x0f, 4(%bp,%di)
312         ret
313         .size set_seg_base, . - set_seg_base
314
315 #endif /* KEEP_IT_REAL */
316
317 /****************************************************************************
318  * copy_bytes (real-mode or 16-bit protected-mode near call)
319  *
320  * Copy bytes
321  *
322  * Parameters:
323  *   %ds:esi : source address
324  *   %es:edi : destination address
325  *   %ecx : length
326  * Returns:
327  *   %ds:esi : next source address
328  *   %es:edi : next destination address
329  * Corrupts:
330  *   None
331  ****************************************************************************
332  */
333         .section ".prefix.lib"
334         .code16
335 copy_bytes:
336         pushl %ecx
337         rep addr32 movsb
338         popl %ecx
339         ret
340         .size copy_bytes, . - copy_bytes
341
342 /****************************************************************************
343  * install_block (real-mode near call)
344  *
345  * Install block to specified address
346  *
347  * Parameters:
348  *   %esi : source physical address (must be a multiple of 16)
349  *   %edi : destination physical address (must be a multiple of 16)
350  *   %ecx : length of (decompressed) data
351  *   %edx : total length of block (including any uninitialised data portion)
352  * Returns:
353  *   %esi : next source physical address (will be a multiple of 16)
354  * Corrupts:
355  *   none
356  ****************************************************************************
357  */
358         .section ".prefix.lib"
359         .code16
360 install_block:
361         
362 #ifdef KEEP_IT_REAL
363
364         /* Preserve registers */
365         pushw   %ds
366         pushw   %es
367         pushl   %ecx
368         pushl   %edi
369         
370         /* Convert %esi and %edi to segment registers */
371         shrl    $4, %esi
372         movw    %si, %ds
373         xorw    %si, %si
374         shrl    $4, %edi
375         movw    %di, %es
376         xorw    %di, %di
377
378 #else /* KEEP_IT_REAL */
379
380         /* Call self in protected mode */
381         pushw   %ax
382         movw    $1f, %ax
383         call    pm_call
384         popw    %ax
385         ret
386 1:
387         /* Preserve registers */
388         pushl   %ecx
389         pushl   %edi
390         
391 #endif /* KEEP_IT_REAL */
392
393         
394 #if COMPRESS
395         /* Decompress source to destination */
396         call    decompress16
397 #else
398         /* Copy source to destination */
399         call    copy_bytes
400 #endif
401
402         /* Zero .bss portion */
403         negl    %ecx
404         addl    %edx, %ecx
405         pushw   %ax
406         xorw    %ax, %ax
407         rep addr32 stosb
408         popw    %ax
409
410         /* Round up %esi to start of next source block */
411         addl    $0xf, %esi
412         andl    $~0xf, %esi
413
414
415 #ifdef KEEP_IT_REAL
416
417         /* Convert %ds:esi back to a physical address */
418         movzwl  %ds, %cx
419         shll    $4, %ecx
420         addl    %ecx, %esi
421
422         /* Restore registers */
423         popl    %edi
424         popl    %ecx
425         popw    %es
426         popw    %ds
427
428 #else /* KEEP_IT_REAL */
429
430         /* Restore registers */
431         popl    %edi
432         popl    %ecx
433
434 #endif
435
436         ret
437         .size install_block, . - install_block
438         
439 /****************************************************************************
440  * alloc_basemem (real-mode near call)
441  *
442  * Allocate space for .text16 and .data16 from top of base memory.
443  * Memory is allocated using the BIOS free base memory counter at
444  * 0x40:13.
445  *
446  * Parameters: 
447  *   none
448  * Returns:
449  *   %ax : .text16 segment address
450  *   %bx : .data16 segment address
451  * Corrupts:
452  *   none
453  ****************************************************************************
454  */
455         .section ".prefix.lib"
456         .code16
457         .globl  alloc_basemem
458 alloc_basemem:
459         /* FBMS => %ax as segment address */
460         movw    $0x40, %ax
461         movw    %ax, %fs
462         movw    %fs:0x13, %ax
463         shlw    $6, %ax
464
465         /* .data16 segment address */
466         subw    $_data16_size_pgh, %ax
467         pushw   %ax
468
469         /* .text16 segment address */
470         subw    $_text16_size_pgh, %ax
471         pushw   %ax
472
473         /* Update FBMS */
474         shrw    $6, %ax
475         movw    %ax, %fs:0x13
476
477         /* Return */
478         popw    %ax
479         popw    %bx
480         ret
481         .size alloc_basemem, . - alloc_basemem
482
483 /****************************************************************************
484  * install (real-mode near call)
485  *
486  * Install all text and data segments.
487  *
488  * Parameters:
489  *   none
490  * Returns:
491  *   %ax  : .text16 segment address
492  *   %bx  : .data16 segment address
493  * Corrupts:
494  *   none
495  ****************************************************************************
496  */
497         .section ".prefix.lib"
498         .code16
499         .globl install
500 install:
501         /* Preserve registers */
502         pushl   %esi
503         pushl   %edi
504         /* Allocate space for .text16 and .data16 */
505         call    alloc_basemem
506         /* Image source = %cs:0000 */
507         xorl    %esi, %esi
508         /* Image destination = HIGHMEM_LOADPOINT */
509         movl    $HIGHMEM_LOADPOINT, %edi
510         /* Install text and data segments */
511         call    install_prealloc
512         /* Restore registers and return */
513         popl    %edi
514         popl    %esi
515         ret
516         .size install, . - install
517
518 /****************************************************************************
519  * install_prealloc (real-mode near call)
520  *
521  * Install all text and data segments.
522  *
523  * Parameters:
524  *   %ax  : .text16 segment address
525  *   %bx  : .data16 segment address
526  *   %esi : Image source physical address (or zero for %cs:0000)
527  *   %edi : Decompression temporary area physical address
528  * Corrupts:
529  *   none
530  ****************************************************************************
531  */
532         .section ".prefix.lib"
533         .code16
534         .globl install_prealloc
535 install_prealloc:
536         /* Save registers */
537         pushal
538         pushw   %ds
539         pushw   %es
540
541         /* Sanity: clear the direction flag asap */
542         cld
543
544         /* Calculate physical address of payload (i.e. first source) */
545         testl   %esi, %esi
546         jnz     1f
547         movw    %cs, %si
548         shll    $4, %esi
549 1:      addl    $_payload_offset, %esi
550
551         /* Install .text16 and .data16 */
552         pushl   %edi
553         movzwl  %ax, %edi
554         shll    $4, %edi
555         movl    $_text16_size, %ecx
556         movl    %ecx, %edx
557         call    install_block           /* .text16 */
558         movzwl  %bx, %edi
559         shll    $4, %edi
560         movl    $_data16_progbits_size, %ecx
561         movl    $_data16_size, %edx
562         call    install_block           /* .data16 */
563         popl    %edi
564
565         /* Set up %ds for access to .data16 */
566         movw    %bx, %ds
567
568 #ifdef KEEP_IT_REAL
569         /* Initialise libkir */
570         movw    %ax, (init_libkir_vector+2)
571         lcall   *init_libkir_vector
572 #else
573         /* Install .text and .data to temporary area in high memory,
574          * prior to reading the E820 memory map and relocating
575          * properly.
576          */
577         movl    $_textdata_progbits_size, %ecx
578         movl    $_textdata_size, %edx
579         call    install_block
580
581         /* Initialise librm at current location */
582         movw    %ax, (init_librm_vector+2)
583         lcall   *init_librm_vector
584
585         /* Call relocate() to determine target address for relocation.
586          * relocate() will return with %esi, %edi and %ecx set up
587          * ready for the copy to the new location.
588          */
589         movw    %ax, (prot_call_vector+2)
590         pushl   $relocate
591         lcall   *prot_call_vector
592         popl    %edx /* discard */
593
594         /* Copy code to new location */
595         pushl   %edi
596         pushw   %ax
597         movw    $copy_bytes, %ax
598         call    pm_call
599         popw    %ax
600         popl    %edi
601
602         /* Initialise librm at new location */
603         lcall   *init_librm_vector
604
605 #endif
606         /* Restore registers */
607         popw    %es
608         popw    %ds
609         popal
610         ret
611         .size install_prealloc, . - install_prealloc
612
613         /* Vectors for far calls to .text16 functions */
614         .section ".data16"
615 #ifdef KEEP_IT_REAL
616 init_libkir_vector:
617         .word init_libkir
618         .word 0
619         .size init_libkir_vector, . - init_libkir_vector
620 #else
621 init_librm_vector:
622         .word init_librm
623         .word 0
624         .size init_librm_vector, . - init_librm_vector
625 prot_call_vector:
626         .word prot_call
627         .word 0
628         .size prot_call_vector, . - prot_call_vector
629 #endif
630
631
632         /* File split information for the compressor */
633 #if COMPRESS
634         .section ".zinfo", "a"
635         .ascii  "COPY"
636         .long   _prefix_load_offset
637         .long   _prefix_progbits_size
638         .long   _max_align
639         .ascii  "PACK"
640         .long   _text16_load_offset
641         .long   _text16_progbits_size
642         .long   _max_align
643         .ascii  "PACK"
644         .long   _data16_load_offset
645         .long   _data16_progbits_size
646         .long   _max_align
647         .ascii  "PACK"
648         .long   _textdata_load_offset
649         .long   _textdata_progbits_size
650         .long   _max_align
651 #else /* COMPRESS */
652         .section ".zinfo", "a"
653         .ascii  "COPY"
654         .long   _prefix_load_offset
655         .long   _load_size
656         .long   _max_align
657 #endif /* COMPRESS */