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