[build] Cope with oddities in the Fedora 10 assembler
[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         .arch i386
21
22 /**
23  * High memory temporary load address
24  *
25  * Temporary buffer into which to copy (or decompress) our runtime
26  * image, prior to calling get_memmap() and relocate().  We don't
27  * actually leave anything here once install() has returned.
28  *
29  * We use the start of an even megabyte so that we don't have to worry
30  * about the current state of the A20 line.
31  *
32  * We use 4MB rather than 2MB because some PXE stack / PMM BIOS
33  * combinations are known to place data required by other UNDI ROMs
34  * loader around the 2MB mark.
35  */
36         .globl  HIGHMEM_LOADPOINT
37         .equ    HIGHMEM_LOADPOINT, ( 4 << 20 )
38
39 /* Image compression enabled */
40 #define COMPRESS 1
41
42 #define CR0_PE 1
43
44 /*****************************************************************************
45  * Utility function: print character (with LF -> LF,CR translation)
46  *
47  * Parameters:
48  *   %al : character to print
49  *   %ds:di : output buffer (or %di=0 to print to console)
50  * Returns:
51  *   %ds:di : next character in output buffer (if applicable)
52  *****************************************************************************
53  */
54         .section ".prefix.lib", "awx", @progbits
55         .code16
56         .globl  print_character
57 print_character:
58         /* Preserve registers */
59         pushw   %ax
60         pushw   %bx
61         pushw   %bp
62         /* If %di is non-zero, write character to buffer and exit */
63         testw   %di, %di
64         jz      1f
65         movb    %al, %ds:(%di)
66         incw    %di
67         jmp     3f
68 1:      /* Print character */
69         movw    $0x0007, %bx            /* page 0, attribute 7 (normal) */
70         movb    $0x0e, %ah              /* write char, tty mode */
71         cmpb    $0x0a, %al              /* '\n'? */
72         jne     2f
73         int     $0x10
74         movb    $0x0d, %al
75 2:      int     $0x10
76         /* Restore registers and return */
77 3:      popw    %bp
78         popw    %bx
79         popw    %ax
80         ret
81         .size   print_character, . - print_character
82
83 /*****************************************************************************
84  * Utility function: print a NUL-terminated string
85  *
86  * Parameters:
87  *   %ds:si : string to print
88  *   %ds:di : output buffer (or %di=0 to print to console)
89  * Returns:
90  *   %ds:si : character after terminating NUL
91  *   %ds:di : next character in output buffer (if applicable)
92  *****************************************************************************
93  */
94         .section ".prefix.lib", "awx", @progbits
95         .code16
96         .globl  print_message
97 print_message:
98         /* Preserve registers */
99         pushw   %ax
100         /* Print string */
101 1:      lodsb
102         testb   %al, %al
103         je      2f
104         call    print_character
105         jmp     1b
106 2:      /* Restore registers and return */
107         popw    %ax
108         ret
109         .size   print_message, . - print_message
110
111 /*****************************************************************************
112  * Utility functions: print hex digit/byte/word/dword
113  *
114  * Parameters:
115  *   %al (low nibble) : digit to print
116  *   %al : byte to print
117  *   %ax : word to print
118  *   %eax : dword to print
119  *   %ds:di : output buffer (or %di=0 to print to console)
120  * Returns:
121  *   %ds:di : next character in output buffer (if applicable)
122  *****************************************************************************
123  */
124         .section ".prefix.lib", "awx", @progbits
125         .code16
126         .globl  print_hex_dword
127 print_hex_dword:
128         rorl    $16, %eax
129         call    print_hex_word
130         rorl    $16, %eax
131         /* Fall through */
132         .size   print_hex_dword, . - print_hex_dword
133         .globl  print_hex_word
134 print_hex_word:
135         xchgb   %al, %ah
136         call    print_hex_byte
137         xchgb   %al, %ah
138         /* Fall through */
139         .size   print_hex_word, . - print_hex_word
140         .globl  print_hex_byte
141 print_hex_byte:
142         rorb    $4, %al
143         call    print_hex_nibble
144         rorb    $4, %al
145         /* Fall through */
146         .size   print_hex_byte, . - print_hex_byte
147         .globl  print_hex_nibble
148 print_hex_nibble:
149         /* Preserve registers */
150         pushw   %ax
151         /* Print digit (technique by Norbert Juffa <norbert.juffa@amd.com> */
152         andb    $0x0f, %al
153         cmpb    $10, %al
154         sbbb    $0x69, %al
155         das
156         call    print_character
157         /* Restore registers and return */
158         popw    %ax
159         ret
160         .size   print_hex_nibble, . - print_hex_nibble
161
162 /*****************************************************************************
163  * Utility function: print PCI bus:dev.fn
164  *
165  * Parameters:
166  *   %ax : PCI bus:dev.fn to print
167  *   %ds:di : output buffer (or %di=0 to print to console)
168  * Returns:
169  *   %ds:di : next character in output buffer (if applicable)
170  *****************************************************************************
171  */
172         .section ".prefix.lib", "awx", @progbits
173         .code16
174         .globl  print_pci_busdevfn
175 print_pci_busdevfn:
176         /* Preserve registers */
177         pushw   %ax
178         /* Print bus */
179         xchgb   %al, %ah
180         call    print_hex_byte
181         /* Print ":" */
182         movb    $( ':' ), %al
183         call    print_character
184         /* Print device */
185         movb    %ah, %al
186         shrb    $3, %al
187         call    print_hex_byte
188         /* Print "." */
189         movb    $( '.' ), %al
190         call    print_character
191         /* Print function */
192         movb    %ah, %al
193         andb    $0x07, %al
194         call    print_hex_nibble
195         /* Restore registers and return */
196         popw    %ax
197         ret
198         .size   print_pci_busdevfn, . - print_pci_busdevfn
199
200 /*****************************************************************************
201  * Utility function: clear current line
202  *
203  * Parameters:
204  *   %ds:di : output buffer (or %di=0 to print to console)
205  * Returns:
206  *   %ds:di : next character in output buffer (if applicable)
207  *****************************************************************************
208  */
209         .section ".prefix.lib", "awx", @progbits
210         .code16
211         .globl  print_kill_line
212 print_kill_line:
213         /* Preserve registers */
214         pushw   %ax
215         pushw   %cx
216         /* Print CR */
217         movb    $( '\r' ), %al
218         call    print_character
219         /* Print 79 spaces */
220         movb    $( ' ' ), %al
221         movw    $79, %cx
222 1:      call    print_character
223         loop    1b
224         /* Print CR */
225         movb    $( '\r' ), %al
226         call    print_character
227         /* Restore registers and return */
228         popw    %cx
229         popw    %ax
230         ret
231         .size   print_kill_line, . - print_kill_line
232
233 /****************************************************************************
234  * pm_call (real-mode near call)
235  *
236  * Call routine in 16-bit protected mode for access to extended memory
237  *
238  * Parameters:
239  *   %ax : address of routine to call in 16-bit protected mode
240  * Returns:
241  *   none
242  * Corrupts:
243  *   %ax
244  *
245  * The specified routine is called in 16-bit protected mode, with:
246  *
247  *   %cs : 16-bit code segment with base matching real-mode %cs
248  *   %ss : 16-bit data segment with base matching real-mode %ss
249  *   %ds,%es,%fs,%gs : 32-bit data segment with zero base and 4GB limit
250  *
251  ****************************************************************************
252  */
253
254 #ifndef KEEP_IT_REAL
255
256         /* GDT for protected-mode calls */
257         .section ".prefix.lib", "awx", @progbits
258         .align 16
259 pm_call_vars:
260 gdt:
261 gdt_limit:              .word gdt_length - 1
262 gdt_base:               .long 0
263                         .word 0 /* padding */
264 pm_cs:          /* 16-bit protected-mode code segment */        
265         .equ    PM_CS, pm_cs - gdt
266         .word   0xffff, 0
267         .byte   0, 0x9b, 0x00, 0
268 pm_ss:          /* 16-bit protected-mode stack segment */
269         .equ    PM_SS, pm_ss - gdt
270         .word   0xffff, 0
271         .byte   0, 0x93, 0x00, 0
272 pm_ds:          /* 32-bit protected-mode flat data segment */
273         .equ    PM_DS, pm_ds - gdt
274         .word   0xffff, 0
275         .byte   0, 0x93, 0xcf, 0
276 gdt_end:
277         .equ    gdt_length, . - gdt
278         .size   gdt, . - gdt
279
280         .section ".prefix.lib", "awx", @progbits
281         .align 16
282 pm_saved_gdt:   
283         .long   0, 0
284         .size   pm_saved_gdt, . - pm_saved_gdt
285
286         .equ    pm_call_vars_size, . - pm_call_vars
287 #define PM_CALL_VAR(x) ( -pm_call_vars_size + ( (x) - pm_call_vars ) )
288
289         .section ".prefix.lib", "awx", @progbits
290         .code16
291 pm_call:
292         /* Preserve registers, flags, and RM return point */
293         pushw   %bp
294         movw    %sp, %bp
295         subw    $pm_call_vars_size, %sp
296         andw    $0xfff0, %sp
297         pushfl
298         pushw   %gs
299         pushw   %fs
300         pushw   %es
301         pushw   %ds
302         pushw   %ss
303         pushw   %cs
304         pushw   $99f
305
306         /* Set up local variable block, and preserve GDT */
307         pushw   %cx
308         pushw   %si
309         pushw   %di
310         pushw   %ss
311         popw    %es
312         movw    $pm_call_vars, %si
313         leaw    PM_CALL_VAR(pm_call_vars)(%bp), %di
314         movw    $pm_call_vars_size, %cx
315         cs rep movsb
316         popw    %di
317         popw    %si
318         popw    %cx
319         sgdt    PM_CALL_VAR(pm_saved_gdt)(%bp)
320
321         /* Set up GDT bases */
322         pushl   %eax
323         pushl   %edi
324         xorl    %eax, %eax
325         movw    %ss, %ax
326         shll    $4, %eax
327         movzwl  %bp, %edi
328         addr32 leal PM_CALL_VAR(gdt)(%eax, %edi), %eax
329         movl    %eax, PM_CALL_VAR(gdt_base)(%bp)
330         movw    %cs, %ax
331         movw    $PM_CALL_VAR(pm_cs), %di
332         call    set_seg_base
333         movw    %ss, %ax
334         movw    $PM_CALL_VAR(pm_ss), %di
335         call    set_seg_base
336         popl    %edi
337         popl    %eax
338
339         /* Switch CPU to protected mode and load up segment registers */
340         pushl   %eax
341         cli
342         data32 lgdt PM_CALL_VAR(gdt)(%bp)
343         movl    %cr0, %eax
344         orb     $CR0_PE, %al
345         movl    %eax, %cr0
346         ljmp    $PM_CS, $1f
347 1:      movw    $PM_SS, %ax
348         movw    %ax, %ss
349         movw    $PM_DS, %ax
350         movw    %ax, %ds
351         movw    %ax, %es
352         movw    %ax, %fs
353         movw    %ax, %gs
354         popl    %eax
355
356         /* Call PM routine */
357         call    *%ax
358
359         /* Set real-mode segment limits on %ds, %es, %fs and %gs */
360         movw    %ss, %ax
361         movw    %ax, %ds
362         movw    %ax, %es
363         movw    %ax, %fs
364         movw    %ax, %gs
365
366         /* Return CPU to real mode */
367         movl    %cr0, %eax
368         andb    $0!CR0_PE, %al
369         movl    %eax, %cr0
370
371         /* Restore registers and flags */
372         lret    /* will ljmp to 99f */
373 99:     popw    %ss
374         popw    %ds
375         popw    %es
376         popw    %fs
377         popw    %gs
378         data32 lgdt PM_CALL_VAR(pm_saved_gdt)(%bp)
379         popfl
380         movw    %bp, %sp
381         popw    %bp
382         ret
383         .size pm_call, . - pm_call
384
385 set_seg_base:
386         rolw    $4, %ax
387         movw    %ax, 2(%bp,%di)
388         andw    $0xfff0, 2(%bp,%di)
389         movb    %al, 4(%bp,%di)
390         andb    $0x0f, 4(%bp,%di)
391         ret
392         .size set_seg_base, . - set_seg_base
393
394 #endif /* KEEP_IT_REAL */
395
396 /****************************************************************************
397  * copy_bytes (real-mode or 16-bit protected-mode near call)
398  *
399  * Copy bytes
400  *
401  * Parameters:
402  *   %ds:esi : source address
403  *   %es:edi : destination address
404  *   %ecx : length
405  * Returns:
406  *   %ds:esi : next source address
407  *   %es:edi : next destination address
408  * Corrupts:
409  *   None
410  ****************************************************************************
411  */
412         .section ".prefix.lib", "awx", @progbits
413         .code16
414 copy_bytes:
415         pushl %ecx
416         rep addr32 movsb
417         popl %ecx
418         ret
419         .size copy_bytes, . - copy_bytes
420
421 /****************************************************************************
422  * install_block (real-mode near call)
423  *
424  * Install block to specified address
425  *
426  * Parameters:
427  *   %esi : source physical address (must be a multiple of 16)
428  *   %edi : destination physical address (must be a multiple of 16)
429  *   %ecx : length of (decompressed) data
430  *   %edx : total length of block (including any uninitialised data portion)
431  * Returns:
432  *   %esi : next source physical address (will be a multiple of 16)
433  * Corrupts:
434  *   none
435  ****************************************************************************
436  */
437         .section ".prefix.lib", "awx", @progbits
438         .code16
439 install_block:
440         
441 #ifdef KEEP_IT_REAL
442
443         /* Preserve registers */
444         pushw   %ds
445         pushw   %es
446         pushl   %ecx
447         pushl   %edi
448         
449         /* Convert %esi and %edi to segment registers */
450         shrl    $4, %esi
451         movw    %si, %ds
452         xorw    %si, %si
453         shrl    $4, %edi
454         movw    %di, %es
455         xorw    %di, %di
456
457 #else /* KEEP_IT_REAL */
458
459         /* Call self in protected mode */
460         pushw   %ax
461         movw    $1f, %ax
462         call    pm_call
463         popw    %ax
464         ret
465 1:
466         /* Preserve registers */
467         pushl   %ecx
468         pushl   %edi
469         
470 #endif /* KEEP_IT_REAL */
471
472         
473 #if COMPRESS
474         /* Decompress source to destination */
475         call    decompress16
476 #else
477         /* Copy source to destination */
478         call    copy_bytes
479 #endif
480
481         /* Zero .bss portion */
482         negl    %ecx
483         addl    %edx, %ecx
484         pushw   %ax
485         xorw    %ax, %ax
486         rep addr32 stosb
487         popw    %ax
488
489         /* Round up %esi to start of next source block */
490         addl    $0xf, %esi
491         andl    $~0xf, %esi
492
493
494 #ifdef KEEP_IT_REAL
495
496         /* Convert %ds:esi back to a physical address */
497         movzwl  %ds, %cx
498         shll    $4, %ecx
499         addl    %ecx, %esi
500
501         /* Restore registers */
502         popl    %edi
503         popl    %ecx
504         popw    %es
505         popw    %ds
506
507 #else /* KEEP_IT_REAL */
508
509         /* Restore registers */
510         popl    %edi
511         popl    %ecx
512
513 #endif
514
515         ret
516         .size install_block, . - install_block
517         
518 /****************************************************************************
519  * alloc_basemem (real-mode near call)
520  *
521  * Allocate space for .text16 and .data16 from top of base memory.
522  * Memory is allocated using the BIOS free base memory counter at
523  * 0x40:13.
524  *
525  * Parameters: 
526  *   none
527  * Returns:
528  *   %ax : .text16 segment address
529  *   %bx : .data16 segment address
530  * Corrupts:
531  *   none
532  ****************************************************************************
533  */
534         .section ".prefix.lib", "awx", @progbits
535         .code16
536         .globl  alloc_basemem
537 alloc_basemem:
538         /* Preserve registers */
539         pushw   %fs
540
541         /* FBMS => %ax as segment address */
542         pushw   $0x40
543         popw    %fs
544         movw    %fs:0x13, %ax
545         shlw    $6, %ax
546
547         /* Calculate .data16 segment address */
548         subw    $_data16_memsz_pgh, %ax
549         pushw   %ax
550
551         /* Calculate .text16 segment address */
552         subw    $_text16_memsz_pgh, %ax
553         pushw   %ax
554
555         /* Update FBMS */
556         shrw    $6, %ax
557         movw    %ax, %fs:0x13
558
559         /* Retrieve .text16 and .data16 segment addresses */
560         popw    %ax
561         popw    %bx
562
563         /* Restore registers and return */
564         popw    %fs
565         ret
566         .size alloc_basemem, . - alloc_basemem
567
568 /****************************************************************************
569  * free_basemem (real-mode near call)
570  *
571  * Free space allocated with alloc_basemem.
572  *
573  * Parameters:
574  *   %ax : .text16 segment address
575  *   %bx : .data16 segment address
576  * Returns:
577  *   %ax : 0 if successfully freed
578  * Corrupts:
579  *   none
580  ****************************************************************************
581  */
582         .section ".text16", "ax", @progbits
583         .code16
584         .globl  free_basemem
585 free_basemem:
586         /* Preserve registers */
587         pushw   %fs
588
589         /* Check FBMS counter */
590         pushw   %ax
591         shrw    $6, %ax
592         pushw   $0x40
593         popw    %fs
594         cmpw    %ax, %fs:0x13
595         popw    %ax
596         jne     1f
597
598         /* Check hooked interrupt count */
599         cmpw    $0, %cs:hooked_bios_interrupts
600         jne     1f
601
602         /* OK to free memory */
603         addw    $_text16_memsz_pgh, %ax
604         addw    $_data16_memsz_pgh, %ax
605         shrw    $6, %ax
606         movw    %ax, %fs:0x13
607         xorw    %ax, %ax
608
609 1:      /* Restore registers and return */
610         popw    %fs
611         ret
612         .size free_basemem, . - free_basemem
613
614         .section ".text16.data", "aw", @progbits
615         .globl  hooked_bios_interrupts
616 hooked_bios_interrupts:
617         .word   0
618         .size   hooked_bios_interrupts, . - hooked_bios_interrupts
619
620 /****************************************************************************
621  * install (real-mode near call)
622  *
623  * Install all text and data segments.
624  *
625  * Parameters:
626  *   none
627  * Returns:
628  *   %ax  : .text16 segment address
629  *   %bx  : .data16 segment address
630  * Corrupts:
631  *   none
632  ****************************************************************************
633  */
634         .section ".prefix.lib", "awx", @progbits
635         .code16
636         .globl install
637 install:
638         /* Preserve registers */
639         pushl   %esi
640         pushl   %edi
641         /* Allocate space for .text16 and .data16 */
642         call    alloc_basemem
643         /* Image source = %cs:0000 */
644         xorl    %esi, %esi
645         /* Image destination = HIGHMEM_LOADPOINT */
646         movl    $HIGHMEM_LOADPOINT, %edi
647         /* Install text and data segments */
648         call    install_prealloc
649         /* Restore registers and return */
650         popl    %edi
651         popl    %esi
652         ret
653         .size install, . - install
654
655 /****************************************************************************
656  * install_prealloc (real-mode near call)
657  *
658  * Install all text and data segments.
659  *
660  * Parameters:
661  *   %ax  : .text16 segment address
662  *   %bx  : .data16 segment address
663  *   %esi : Image source physical address (or zero for %cs:0000)
664  *   %edi : Decompression temporary area physical address
665  * Corrupts:
666  *   none
667  ****************************************************************************
668  */
669         .section ".prefix.lib", "awx", @progbits
670         .code16
671         .globl install_prealloc
672 install_prealloc:
673         /* Save registers */
674         pushal
675         pushw   %ds
676         pushw   %es
677
678         /* Sanity: clear the direction flag asap */
679         cld
680
681         /* Calculate physical address of payload (i.e. first source) */
682         testl   %esi, %esi
683         jnz     1f
684         movw    %cs, %si
685         shll    $4, %esi
686 1:      addl    $_payload_lma, %esi
687
688         /* Install .text16 and .data16 */
689         pushl   %edi
690         movzwl  %ax, %edi
691         shll    $4, %edi
692         movl    $_text16_memsz, %ecx
693         movl    %ecx, %edx
694         call    install_block           /* .text16 */
695         movzwl  %bx, %edi
696         shll    $4, %edi
697         movl    $_data16_filesz, %ecx
698         movl    $_data16_memsz, %edx
699         call    install_block           /* .data16 */
700         popl    %edi
701
702         /* Set up %ds for access to .data16 */
703         movw    %bx, %ds
704
705 #ifdef KEEP_IT_REAL
706         /* Initialise libkir */
707         movw    %ax, (init_libkir_vector+2)
708         lcall   *init_libkir_vector
709 #else
710         /* Install .text and .data to temporary area in high memory,
711          * prior to reading the E820 memory map and relocating
712          * properly.
713          */
714         movl    $_textdata_filesz, %ecx
715         movl    $_textdata_memsz, %edx
716         call    install_block
717
718         /* Initialise librm at current location */
719         movw    %ax, (init_librm_vector+2)
720         lcall   *init_librm_vector
721
722         /* Call relocate() to determine target address for relocation.
723          * relocate() will return with %esi, %edi and %ecx set up
724          * ready for the copy to the new location.
725          */
726         movw    %ax, (prot_call_vector+2)
727         pushl   $relocate
728         lcall   *prot_call_vector
729         popl    %edx /* discard */
730
731         /* Copy code to new location */
732         pushl   %edi
733         pushw   %ax
734         movw    $copy_bytes, %ax
735         call    pm_call
736         popw    %ax
737         popl    %edi
738
739         /* Initialise librm at new location */
740         lcall   *init_librm_vector
741
742 #endif
743         /* Restore registers */
744         popw    %es
745         popw    %ds
746         popal
747         ret
748         .size install_prealloc, . - install_prealloc
749
750         /* Vectors for far calls to .text16 functions */
751         .section ".data16", "aw", @progbits
752 #ifdef KEEP_IT_REAL
753 init_libkir_vector:
754         .word init_libkir
755         .word 0
756         .size init_libkir_vector, . - init_libkir_vector
757 #else
758 init_librm_vector:
759         .word init_librm
760         .word 0
761         .size init_librm_vector, . - init_librm_vector
762 prot_call_vector:
763         .word prot_call
764         .word 0
765         .size prot_call_vector, . - prot_call_vector
766 #endif
767
768 /****************************************************************************
769  * uninstall (real-mode near call)
770  *
771  * Uninstall all text and data segments.
772  *
773  * Parameters:
774  *   %ax  : .text16 segment address
775  *   %bx  : .data16 segment address
776  * Returns:
777  *   none
778  * Corrupts:
779  *   none
780  ****************************************************************************
781  */
782         .section ".text16", "ax", @progbits
783         .code16
784         .globl uninstall
785 uninstall:
786         call    free_basemem
787         ret
788         .size uninstall, . - uninstall
789
790
791
792         /* File split information for the compressor */
793 #if COMPRESS
794         .section ".zinfo", "a", @progbits
795         .ascii  "COPY"
796         .long   _prefix_lma
797         .long   _prefix_filesz
798         .long   _max_align
799         .ascii  "PACK"
800         .long   _text16_lma
801         .long   _text16_filesz
802         .long   _max_align
803         .ascii  "PACK"
804         .long   _data16_lma
805         .long   _data16_filesz
806         .long   _max_align
807         .ascii  "PACK"
808         .long   _textdata_lma
809         .long   _textdata_filesz
810         .long   _max_align
811 #else /* COMPRESS */
812         .section ".zinfo", "a", @progbits
813         .ascii  "COPY"
814         .long   _prefix_lma
815         .long   _filesz
816         .long   _max_align
817 #endif /* COMPRESS */