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