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