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