[prefix] Use area at top of INT 15,88 memory map for temporary decompression
[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         /* Install text and data segments */
447         call    install_prealloc
448         /* Restore registers and return */
449         popl    %edi
450         popl    %esi
451         ret
452         .size install, . - install
453
454 /****************************************************************************
455  * install_prealloc
456  *
457  * Install all text and data segments.
458  *
459  * Parameters:
460  *   %ax  : .text16 segment address
461  *   %bx  : .data16 segment address
462  *   %esi : Image source physical address (or zero for %cs:0000)
463  *   %edi : Decompression temporary area physical address (or zero for default)
464  * Corrupts:
465  *   none
466  ****************************************************************************
467  */
468         .section ".prefix.lib", "awx", @progbits
469         .code16
470         .globl install_prealloc
471 install_prealloc:
472         /* Save registers */
473         pushal
474         pushw   %ds
475         pushw   %es
476
477         /* Sanity: clear the direction flag asap */
478         cld
479
480         /* Copy decompression temporary area physical address to %ebp */
481         movl    %edi, %ebp
482
483         /* Install .text16.early */
484         pushl   %esi
485         xorl    %esi, %esi
486         movw    %cs, %si
487         shll    $4, %esi
488         addl    $_text16_early_lma, %esi
489         movzwl  %ax, %edi
490         shll    $4, %edi
491         movl    $_text16_early_filesz, %ecx
492         movl    $_text16_early_memsz, %edx
493         call    install_block           /* .text16.early */
494         popl    %esi
495
496         /* Open up access to payload */
497 #ifndef KEEP_IT_REAL
498         /* Access high memory */
499         pushw   %cs
500         pushw   $1f
501         pushw   %ax
502         pushw   $access_highmem
503         lret
504 1:      /* Die if we could not access high memory */
505         jnc     3f
506         movw    $a20_death_message, %si
507         xorw    %di, %di
508         call    print_message
509 2:      jmp     2b
510         .section ".prefix.data", "aw", @progbits
511 a20_death_message:
512         .asciz  "Gate A20 stuck - cannot continue\n"
513         .size   a20_death_message, . - a20_death_message
514         .previous
515 3:
516 #endif
517
518         /* Calculate physical address of payload (i.e. first source) */
519         testl   %esi, %esi
520         jnz     1f
521         movw    %cs, %si
522         shll    $4, %esi
523 1:      addl    %cs:payload_lma, %esi
524
525         /* Install .text16.late and .data16 */
526         movl    $_text16_late_filesz, %ecx
527         movl    $_text16_late_memsz, %edx
528         call    install_block           /* .text16.late */
529         movzwl  %bx, %edi
530         shll    $4, %edi
531         movl    $_data16_filesz, %ecx
532         movl    $_data16_memsz, %edx
533         call    install_block           /* .data16 */
534
535         /* Set up %ds for access to .data16 */
536         movw    %bx, %ds
537
538 #ifdef KEEP_IT_REAL
539         /* Initialise libkir */
540         movw    %ax, (init_libkir_vector+2)
541         lcall   *init_libkir_vector
542 #else
543         /* Find a suitable decompression temporary area, if none specified */
544         testl   %ebp, %ebp
545         jnz     1f
546         /* Use INT 15,88 to find the highest available address via INT
547          * 15,88.  This limits us to around 64MB, which should avoid
548          * all of the POST-time memory map failure modes.
549          */
550         pushl   %eax
551         movb    $0x88, %ah
552         int     $0x15
553         movw    %ax, %bp
554         addl    $0x400, %ebp
555         subl    $_textdata_memsz_kb, %ebp
556         shll    $10, %ebp
557         popl    %eax
558 1:
559         /* Install .text and .data to temporary area in high memory,
560          * prior to reading the E820 memory map and relocating
561          * properly.
562          */
563         movl    %ebp, %edi
564         movl    $_textdata_filesz, %ecx
565         movl    $_textdata_memsz, %edx
566         call    install_block
567
568         /* Initialise librm at current location */
569         movw    %ax, (init_librm_vector+2)
570         movl    %ebp, %edi
571         lcall   *init_librm_vector
572
573         /* Call relocate() to determine target address for relocation.
574          * relocate() will return with %esi, %edi and %ecx set up
575          * ready for the copy to the new location.
576          */
577         movw    %ax, (prot_call_vector+2)
578         pushl   $relocate
579         lcall   *prot_call_vector
580         popl    %edx /* discard */
581
582         /* Copy code to new location */
583         pushl   %edi
584         xorw    %ax, %ax
585         movw    %ax, %es
586         es rep addr32 movsb
587         popl    %edi
588
589         /* Initialise librm at new location */
590         lcall   *init_librm_vector
591 #endif
592
593         /* Restore registers */
594         popw    %es
595         popw    %ds
596         popal
597         ret
598         .size install_prealloc, . - install_prealloc
599
600         /* Vectors for far calls to .text16 functions.  Must be in
601          * .data16, since .prefix may not be writable.
602          */
603         .section ".data16", "aw", @progbits
604 #ifdef KEEP_IT_REAL
605 init_libkir_vector:
606         .word init_libkir
607         .word 0
608         .size init_libkir_vector, . - init_libkir_vector
609 #else
610 init_librm_vector:
611         .word init_librm
612         .word 0
613         .size init_librm_vector, . - init_librm_vector
614 prot_call_vector:
615         .word prot_call
616         .word 0
617         .size prot_call_vector, . - prot_call_vector
618 #endif
619
620         /* Payload address */
621         .section ".prefix.lib", "awx", @progbits
622 payload_lma:
623         .long 0
624         .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
625         .ascii  "ADHL"
626         .long   payload_lma
627         .long   1
628         .long   0
629         .previous
630
631 /****************************************************************************
632  * uninstall
633  *
634  * Uninstall all text and data segments.
635  *
636  * Parameters:
637  *   %ax  : .text16 segment address
638  *   %bx  : .data16 segment address
639  * Returns:
640  *   none
641  * Corrupts:
642  *   none
643  ****************************************************************************
644  */
645         .section ".text16", "ax", @progbits
646         .code16
647         .globl uninstall
648 uninstall:
649         call    free_basemem
650         ret
651         .size uninstall, . - uninstall
652
653
654
655         /* File split information for the compressor */
656 #if COMPRESS
657 #define PACK_OR_COPY    "PACK"
658 #else
659 #define PACK_OR_COPY    "COPY"
660 #endif
661         .section ".zinfo", "a", @progbits
662         .ascii  "COPY"
663         .long   _prefix_lma
664         .long   _prefix_filesz
665         .long   _max_align
666         .ascii  PACK_OR_COPY
667         .long   _text16_early_lma
668         .long   _text16_early_filesz
669         .long   _max_align
670         .ascii  "PAYL"
671         .long   0
672         .long   0
673         .long   _max_align
674         .ascii  PACK_OR_COPY
675         .long   _text16_late_lma
676         .long   _text16_late_filesz
677         .long   _max_align
678         .ascii  PACK_OR_COPY
679         .long   _data16_lma
680         .long   _data16_filesz
681         .long   _max_align
682         .ascii  PACK_OR_COPY
683         .long   _textdata_lma
684         .long   _textdata_filesz
685         .long   _max_align