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