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