b820f2a36ce322335e51107ab741c422f16b3f50
[people/meteger/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
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 ".prefix.lib", "awx", @progbits
252         .align 16
253 flatten_vars:
254 flatten_gdt:
255 flatten_gdt_limit:      .word flatten_gdt_length - 1
256 flatten_gdt_base:       .long 0
257                         .word 0 /* padding */
258 flatten_cs:     /* 16-bit protected-mode flat code segment */
259         .equ    FLAT_CS, flatten_cs - flatten_gdt
260         .word   0xffff, 0
261         .byte   0, 0x9b, 0x8f, 0
262 flatten_ss:     /* 16-bit protected-mode flat stack segment */
263         .equ    FLAT_SS, flatten_ss - flatten_gdt
264         .word   0xffff, 0
265         .byte   0, 0x93, 0x8f, 0
266 flatten_gdt_end:
267         .equ    flatten_gdt_length, . - flatten_gdt
268         .size   flatten_gdt, . - flatten_gdt
269
270         .section ".prefix.lib", "awx", @progbits
271         .align 16
272 flatten_saved_gdt:
273         .long   0, 0
274         .size   flatten_saved_gdt, . - flatten_saved_gdt
275
276         .equ    flatten_vars_size, . - flatten_vars
277 #define FLATTEN_VAR(x) ( -flatten_vars_size + ( (x) - flatten_vars ) )
278
279         .section ".prefix.lib", "awx", @progbits
280         .code16
281 flatten_real_mode:
282         /* Preserve registers and flags, allocate local variable block */
283         pushw   %bp
284         movw    %sp, %bp
285         subw    $flatten_vars_size, %sp
286         andw    $0xfff0, %sp
287         pushfl
288         pushl   %eax
289         pushl   %edi
290         pushw   %si
291         pushw   %cx
292         pushw   %gs
293         pushw   %fs
294         pushw   %es
295         pushw   %ds
296         pushw   %ss
297
298         /* Fill local variable block and preserve GDT */
299         pushw   %ss
300         popw    %es
301         movw    $flatten_vars, %si
302         leaw    FLATTEN_VAR(flatten_vars)(%bp), %di
303         movw    $flatten_vars_size, %cx
304         cs rep movsb
305         sgdt    FLATTEN_VAR(flatten_saved_gdt)(%bp)
306
307         /* Set up GDT bases */
308         xorl    %eax, %eax
309         movw    %ss, %ax
310         shll    $4, %eax
311         movzwl  %bp, %edi
312         addr32 leal FLATTEN_VAR(flatten_gdt)(%eax, %edi), %eax
313         movl    %eax, FLATTEN_VAR(flatten_gdt_base)(%bp)
314         movw    %cs, %ax
315         movw    $FLATTEN_VAR(flatten_cs), %di
316         call    set_seg_base
317         movw    %ss, %ax
318         movw    $FLATTEN_VAR(flatten_ss), %di
319         call    set_seg_base
320
321         /* Switch temporarily to protected mode and set segment registers */
322         pushw   %cs
323         pushw   $2f
324         cli
325         data32 lgdt FLATTEN_VAR(flatten_gdt)(%bp)
326         movl    %cr0, %eax
327         orb     $CR0_PE, %al
328         movl    %eax, %cr0
329         ljmp    $FLAT_CS, $1f
330 1:      movw    $FLAT_SS, %ax
331         movw    %ax, %ss
332         movw    %ax, %ds
333         movw    %ax, %es
334         movw    %ax, %fs
335         movw    %ax, %gs
336         movl    %cr0, %eax
337         andb    $0!CR0_PE, %al
338         movl    %eax, %cr0
339         lret
340 2:      /* lret will ljmp to here */
341
342         /* Restore GDT, registers and flags */
343         data32 lgdt FLATTEN_VAR(flatten_saved_gdt)(%bp)
344         popw    %ss
345         popw    %ds
346         popw    %es
347         popw    %fs
348         popw    %gs
349         popw    %cx
350         popw    %si
351         popl    %edi
352         popl    %eax
353         popfl
354         movw    %bp, %sp
355         popw    %bp
356         ret
357         .size flatten_real_mode, . - flatten_real_mode
358
359 set_seg_base:
360         rolw    $4, %ax
361         movw    %ax, 2(%bp,%di)
362         andw    $0xfff0, 2(%bp,%di)
363         movb    %al, 4(%bp,%di)
364         andb    $0x0f, 4(%bp,%di)
365         ret
366         .size set_seg_base, . - set_seg_base
367
368 #endif /* KEEP_IT_REAL */
369
370 /****************************************************************************
371  * copy_bytes
372  *
373  * Copy bytes
374  *
375  * Parameters:
376  *   %ds:esi : source address
377  *   %es:edi : destination address
378  *   %ecx : length
379  * Returns:
380  *   %ds:esi : next source address
381  *   %es:edi : next destination address
382  * Corrupts:
383  *   None
384  ****************************************************************************
385  */
386 #if ! COMPRESS
387         .section ".prefix.lib", "awx", @progbits
388         .code16
389 copy_bytes:
390         pushl %ecx
391         rep addr32 movsb
392         popl %ecx
393         ret
394         .size copy_bytes, . - copy_bytes
395 #endif /* COMPRESS */
396
397 /****************************************************************************
398  * install_block
399  *
400  * Install block to specified address
401  *
402  * Parameters:
403  *   %esi : source physical address (must be a multiple of 16)
404  *   %edi : destination physical address (must be a multiple of 16)
405  *   %ecx : length of (decompressed) data
406  *   %edx : total length of block (including any uninitialised data portion)
407  * Returns:
408  *   %esi : next source physical address (will be a multiple of 16)
409  *   %edi : next destination physical address (will be a multiple of 16)
410  * Corrupts:
411  *   none
412  ****************************************************************************
413  */
414         .section ".prefix.lib", "awx", @progbits
415         .code16
416 install_block:
417         /* Preserve registers */
418         pushw   %ds
419         pushw   %es
420         pushl   %ecx
421         
422         /* Convert %esi and %edi to %ds:esi and %es:edi */
423         shrl    $4, %esi
424         movw    %si, %ds
425         xorw    %si, %si
426         shll    $4, %esi
427         shrl    $4, %edi
428         movw    %di, %es
429         xorw    %di, %di
430         shll    $4, %edi
431
432 #if COMPRESS
433         /* Decompress source to destination */
434         call    decompress16
435 #else
436         /* Copy source to destination */
437         call    copy_bytes
438 #endif
439
440         /* Zero .bss portion */
441         negl    %ecx
442         addl    %edx, %ecx
443         pushw   %ax
444         xorw    %ax, %ax
445         rep addr32 stosb
446         popw    %ax
447
448         /* Round up %esi and %edi to start of next blocks */
449         addl    $0xf, %esi
450         andl    $~0xf, %esi
451         addl    $0xf, %edi
452         andl    $~0xf, %edi
453
454         /* Convert %ds:esi and %es:edi back to physical addresses */
455         xorl    %ecx, %ecx
456         movw    %ds, %cx
457         shll    $4, %ecx
458         addl    %ecx, %esi
459         xorl    %ecx, %ecx
460         movw    %es, %cx
461         shll    $4, %ecx
462         addl    %ecx, %edi
463
464         /* Restore registers and return */
465         popl    %ecx
466         popw    %es
467         popw    %ds
468         ret
469         .size install_block, . - install_block
470
471 /****************************************************************************
472  * alloc_basemem
473  *
474  * Allocate space for .text16 and .data16 from top of base memory.
475  * Memory is allocated using the BIOS free base memory counter at
476  * 0x40:13.
477  *
478  * Parameters: 
479  *   none
480  * Returns:
481  *   %ax : .text16 segment address
482  *   %bx : .data16 segment address
483  * Corrupts:
484  *   none
485  ****************************************************************************
486  */
487         .section ".prefix.lib", "awx", @progbits
488         .code16
489         .globl  alloc_basemem
490 alloc_basemem:
491         /* Preserve registers */
492         pushw   %fs
493
494         /* FBMS => %ax as segment address */
495         pushw   $0x40
496         popw    %fs
497         movw    %fs:0x13, %ax
498         shlw    $6, %ax
499
500         /* Calculate .data16 segment address */
501         subw    $_data16_memsz_pgh, %ax
502         pushw   %ax
503
504         /* Calculate .text16 segment address */
505         subw    $_text16_memsz_pgh, %ax
506         pushw   %ax
507
508         /* Update FBMS */
509         shrw    $6, %ax
510         movw    %ax, %fs:0x13
511
512         /* Retrieve .text16 and .data16 segment addresses */
513         popw    %ax
514         popw    %bx
515
516         /* Restore registers and return */
517         popw    %fs
518         ret
519         .size alloc_basemem, . - alloc_basemem
520
521 /****************************************************************************
522  * free_basemem
523  *
524  * Free space allocated with alloc_basemem.
525  *
526  * Parameters:
527  *   %ax : .text16 segment address
528  *   %bx : .data16 segment address
529  * Returns:
530  *   %ax : 0 if successfully freed
531  * Corrupts:
532  *   none
533  ****************************************************************************
534  */
535         .section ".text16", "ax", @progbits
536         .code16
537         .globl  free_basemem
538 free_basemem:
539         /* Preserve registers */
540         pushw   %fs
541
542         /* Check FBMS counter */
543         pushw   %ax
544         shrw    $6, %ax
545         pushw   $0x40
546         popw    %fs
547         cmpw    %ax, %fs:0x13
548         popw    %ax
549         jne     1f
550
551         /* Check hooked interrupt count */
552         cmpw    $0, %cs:hooked_bios_interrupts
553         jne     1f
554
555         /* OK to free memory */
556         addw    $_text16_memsz_pgh, %ax
557         addw    $_data16_memsz_pgh, %ax
558         shrw    $6, %ax
559         movw    %ax, %fs:0x13
560         xorw    %ax, %ax
561
562 1:      /* Restore registers and return */
563         popw    %fs
564         ret
565         .size free_basemem, . - free_basemem
566
567         .section ".text16.data", "aw", @progbits
568         .globl  hooked_bios_interrupts
569 hooked_bios_interrupts:
570         .word   0
571         .size   hooked_bios_interrupts, . - hooked_bios_interrupts
572
573 /****************************************************************************
574  * install
575  *
576  * Install all text and data segments.
577  *
578  * Parameters:
579  *   none
580  * Returns:
581  *   %ax  : .text16 segment address
582  *   %bx  : .data16 segment address
583  * Corrupts:
584  *   none
585  ****************************************************************************
586  */
587         .section ".prefix.lib", "awx", @progbits
588         .code16
589         .globl install
590 install:
591         /* Preserve registers */
592         pushl   %esi
593         pushl   %edi
594         /* Allocate space for .text16 and .data16 */
595         call    alloc_basemem
596         /* Image source = %cs:0000 */
597         xorl    %esi, %esi
598         /* Image destination = HIGHMEM_LOADPOINT */
599         movl    $HIGHMEM_LOADPOINT, %edi
600         /* Install text and data segments */
601         call    install_prealloc
602         /* Restore registers and return */
603         popl    %edi
604         popl    %esi
605         ret
606         .size install, . - install
607
608 /****************************************************************************
609  * install_prealloc
610  *
611  * Install all text and data segments.
612  *
613  * Parameters:
614  *   %ax  : .text16 segment address
615  *   %bx  : .data16 segment address
616  *   %esi : Image source physical address (or zero for %cs:0000)
617  *   %edi : Decompression temporary area physical address
618  * Corrupts:
619  *   none
620  ****************************************************************************
621  */
622         .section ".prefix.lib", "awx", @progbits
623         .code16
624         .globl install_prealloc
625 install_prealloc:
626         /* Save registers */
627         pushal
628         pushw   %ds
629         pushw   %es
630
631         /* Sanity: clear the direction flag asap */
632         cld
633
634         /* Copy decompression temporary area physical address to %ebp */
635         movl    %edi, %ebp
636
637         /* Install .text16.early */
638         pushl   %esi
639         xorl    %esi, %esi
640         movw    %cs, %si
641         shll    $4, %esi
642         addl    $_text16_early_lma, %esi
643         movzwl  %ax, %edi
644         shll    $4, %edi
645         movl    $_text16_early_filesz, %ecx
646         movl    $_text16_early_memsz, %edx
647         call    install_block           /* .text16.early */
648         popl    %esi
649
650         /* Open up access to payload */
651 #ifndef KEEP_IT_REAL
652         /* Flatten real mode */
653         call    flatten_real_mode
654 #endif
655
656         /* Calculate physical address of payload (i.e. first source) */
657         testl   %esi, %esi
658         jnz     1f
659         movw    %cs, %si
660         shll    $4, %esi
661 1:      addl    %cs:payload_lma, %esi
662
663         /* Install .text16.late and .data16 */
664         movl    $_text16_late_filesz, %ecx
665         movl    $_text16_late_memsz, %edx
666         call    install_block           /* .text16.late */
667         movzwl  %bx, %edi
668         shll    $4, %edi
669         movl    $_data16_filesz, %ecx
670         movl    $_data16_memsz, %edx
671         call    install_block           /* .data16 */
672
673         /* Set up %ds for access to .data16 */
674         movw    %bx, %ds
675
676 #ifdef KEEP_IT_REAL
677         /* Initialise libkir */
678         movw    %ax, (init_libkir_vector+2)
679         lcall   *init_libkir_vector
680 #else
681         /* Install .text and .data to temporary area in high memory,
682          * prior to reading the E820 memory map and relocating
683          * properly.
684          */
685         movl    %ebp, %edi
686         movl    $_textdata_filesz, %ecx
687         movl    $_textdata_memsz, %edx
688         call    install_block
689
690         /* Initialise librm at current location */
691         movw    %ax, (init_librm_vector+2)
692         movl    %ebp, %edi
693         lcall   *init_librm_vector
694
695         /* Call relocate() to determine target address for relocation.
696          * relocate() will return with %esi, %edi and %ecx set up
697          * ready for the copy to the new location.
698          */
699         movw    %ax, (prot_call_vector+2)
700         pushl   $relocate
701         lcall   *prot_call_vector
702         popl    %edx /* discard */
703
704         /* Copy code to new location */
705         xorw    %ax, %ax
706         movw    %ax, %es
707         movl    %ebp, %edi
708         es rep addr32 movsb
709
710         /* Initialise librm at new location */
711         movl    %ebp, %edi
712         lcall   *init_librm_vector
713 #endif
714
715         /* Restore registers */
716         popw    %es
717         popw    %ds
718         popal
719         ret
720         .size install_prealloc, . - install_prealloc
721
722         /* Vectors for far calls to .text16 functions.  Must be in
723          * .data16, since .prefix may not be writable.
724          */
725         .section ".data16", "aw", @progbits
726 #ifdef KEEP_IT_REAL
727 init_libkir_vector:
728         .word init_libkir
729         .word 0
730         .size init_libkir_vector, . - init_libkir_vector
731 #else
732 init_librm_vector:
733         .word init_librm
734         .word 0
735         .size init_librm_vector, . - init_librm_vector
736 prot_call_vector:
737         .word prot_call
738         .word 0
739         .size prot_call_vector, . - prot_call_vector
740 #endif
741
742         /* Payload address */
743         .section ".prefix.lib", "awx", @progbits
744 payload_lma:
745         .long 0
746         .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
747         .ascii  "ADHL"
748         .long   payload_lma
749         .long   1
750         .long   0
751         .previous
752
753 /****************************************************************************
754  * uninstall
755  *
756  * Uninstall all text and data segments.
757  *
758  * Parameters:
759  *   %ax  : .text16 segment address
760  *   %bx  : .data16 segment address
761  * Returns:
762  *   none
763  * Corrupts:
764  *   none
765  ****************************************************************************
766  */
767         .section ".text16", "ax", @progbits
768         .code16
769         .globl uninstall
770 uninstall:
771         call    free_basemem
772         ret
773         .size uninstall, . - uninstall
774
775
776
777         /* File split information for the compressor */
778 #if COMPRESS
779 #define PACK_OR_COPY    "PACK"
780 #else
781 #define PACK_OR_COPY    "COPY"
782 #endif
783         .section ".zinfo", "a", @progbits
784         .ascii  "COPY"
785         .long   _prefix_lma
786         .long   _prefix_filesz
787         .long   _max_align
788         .ascii  PACK_OR_COPY
789         .long   _text16_early_lma
790         .long   _text16_early_filesz
791         .long   _max_align
792         .ascii  "PAYL"
793         .long   0
794         .long   0
795         .long   _max_align
796         .ascii  PACK_OR_COPY
797         .long   _text16_late_lma
798         .long   _text16_late_filesz
799         .long   _max_align
800         .ascii  PACK_OR_COPY
801         .long   _data16_lma
802         .long   _data16_filesz
803         .long   _max_align
804         .ascii  PACK_OR_COPY
805         .long   _textdata_lma
806         .long   _textdata_filesz
807         .long   _max_align