Use full protected mode for access to high memory within prefix, to
[people/balajirrao/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 /**
21  * High memory temporary load address
22  *
23  * Temporary buffer into which to copy (or decompress) our runtime
24  * image, prior to calling get_memmap() and relocate().  We don't
25  * actually leave anything here once install() has returned.
26  *
27  * We use the start of an even megabyte so that we don't have to worry
28  * about the current state of the A20 line.
29  *
30  * We use 4MB rather than 2MB because there is at least one commercial
31  * PXE ROM ("Broadcom UNDI, PXE-2.1 (build 082) v2.0.4") which stores
32  * data required by the UNDI ROM loader (yes, the ROM loader; that's
33  * the component which should be impossible to damage short of
34  * screwing with the MMU) around the 2MB mark.  Sadly, this is not a
35  * joke.
36  *
37  */
38 #define HIGHMEM_LOADPOINT ( 4 << 20 )
39
40 /* Image compression enabled */
41 #define COMPRESS 1
42
43 #define CR0_PE 1
44
45         .arch i386
46         .section ".prefix.lib", "awx", @progbits
47         .section ".data16", "aw", @progbits
48
49 /****************************************************************************
50  * pm_call (real-mode near call)
51  *
52  * Call routine in 16-bit protected mode for access to extended memory
53  *
54  * Parameters:
55  *   %ax : address of routine to call in 16-bit protected mode
56  * Returns:
57  *   none
58  * Corrupts:
59  *   %ax
60  *
61  * The specified routine is called in 16-bit protected mode, with:
62  *
63  *   %cs : 16-bit code segment with base matching real-mode %cs
64  *   %ss : 16-bit data segment with base matching real-mode %ss
65  *   %ds,%es,%fs,%gs : 32-bit data segment with zero base and 4GB limit
66  *
67  ****************************************************************************
68  */
69
70 #ifndef KEEP_IT_REAL
71
72         /* GDT for protected-mode calls */
73         .section ".data16"
74         .align 16
75 gdt:
76 gdt_limit:              .word gdt_length - 1
77 gdt_base:               .long 0
78                         .word 0 /* padding */
79 pm_cs:          /* 16-bit protected-mode code segment */
80         .equ    PM_CS, pm_cs - gdt
81         .word   0xffff, 0
82         .byte   0, 0x9b, 0x00, 0
83 pm_ss:          /* 16-bit protected-mode stack segment */
84         .equ    PM_SS, pm_ss - gdt
85         .word   0xffff, 0
86         .byte   0, 0x93, 0x00, 0
87 pm_ds:          /* 32-bit protected-mode flat data segment */
88         .equ    PM_DS, pm_ds - gdt
89         .word   0xffff, 0
90         .byte   0, 0x93, 0xcf, 0
91 gdt_end:
92         .equ    gdt_length, . - gdt
93         .size   gdt, . - gdt
94
95         .section ".data16"
96         .align 16
97 pm_saved_gdt:   
98         .long   0, 0
99         .size   pm_saved_gdt, . - pm_saved_gdt
100
101         .section ".prefix.lib"
102         .code16
103 pm_call:
104         /* Preserve registers, flags, GDT, and RM return point */
105         pushfl
106         sgdt    pm_saved_gdt
107         pushw   %gs
108         pushw   %fs
109         pushw   %es
110         pushw   %ds
111         pushw   %ss
112         pushw   %cs
113         pushw   $99f
114
115         /* Set up GDT bases */
116         pushl   %eax
117         pushw   %bx
118         xorl    %eax, %eax
119         movw    %ds, %ax
120         shll    $4, %eax
121         addl    $gdt, %eax
122         movl    %eax, gdt_base
123         movw    %cs, %ax
124         movw    $pm_cs, %bx
125         call    set_seg_base
126         movw    %ss, %ax
127         movw    $pm_ss, %bx
128         call    set_seg_base
129         popw    %bx
130         popl    %eax
131
132         /* Switch CPU to protected mode and load up segment registers */
133         pushl   %eax
134         cli
135         lgdt    gdt
136         movl    %cr0, %eax
137         orb     $CR0_PE, %al
138         movl    %eax, %cr0
139         ljmp    $PM_CS, $1f
140 1:      movw    $PM_SS, %ax
141         movw    %ax, %ss
142         movw    $PM_DS, %ax
143         movw    %ax, %ds
144         movw    %ax, %es
145         movw    %ax, %fs
146         movw    %ax, %gs
147         popl    %eax
148
149         /* Call PM routine */
150         call    *%ax
151
152         /* Set real-mode segment limits on %ds, %es, %fs and %gs */
153         movw    %ss, %ax
154         movw    %ax, %ds
155         movw    %ax, %es
156         movw    %ax, %fs
157         movw    %ax, %gs
158
159         /* Return CPU to real mode */
160         movl    %cr0, %eax
161         andb    $0!CR0_PE, %al
162         movl    %eax, %cr0
163
164         /* Restore registers and flags */
165         lret
166 99:     popw    %ss
167         popw    %ds
168         popw    %es
169         popw    %fs
170         popw    %gs
171         lgdt    pm_saved_gdt
172         popfl
173
174         ret
175         .size pm_call, . - pm_call
176
177 set_seg_base:
178         rolw    $4, %ax
179         movw    %ax, 2(%bx)
180         andw    $0xfff0, 2(%bx)
181         movb    %al, 4(%bx)
182         andb    $0x0f, 4(%bx)
183         ret
184         .size set_seg_base, . - set_seg_base
185
186 #endif /* KEEP_IT_REAL */
187
188 /****************************************************************************
189  * copy_bytes (real-mode or 16-bit protected-mode near call)
190  *
191  * Copy bytes
192  *
193  * Parameters:
194  *   %ds:esi : source address
195  *   %es:edi : destination address
196  *   %ecx : length
197  * Returns:
198  *   %ds:esi : next source address
199  *   %ds:esi : next destination address
200  * Corrupts:
201  *   None
202  ****************************************************************************
203  */
204         .section ".prefix.lib"
205         .code16
206 copy_bytes:
207         pushl %ecx
208         rep addr32 movsb
209         popl %ecx
210         ret
211         .size copy_bytes, . - copy_bytes
212
213 /****************************************************************************
214  * install_block (real-mode or 16-bit protected-mode near call)
215  *
216  * Install block to specified address
217  *
218  * Parameters:
219  *   %ds:esi : source address (must be a multiple of 16)
220  *   %es:edi : destination address
221  *   %ecx : length of (decompressed) data
222  *   %edx : total length of block (including any uninitialised data portion)
223  * Returns:
224  *   %ds:esi : next source address (will be a multiple of 16)
225  * Corrupts:
226  *   %edi, %ecx, %edx
227  ****************************************************************************
228  */
229         .section ".prefix.lib"
230         .code16
231 install_block:
232 #if COMPRESS
233         /* Decompress source to destination */
234         call    decompress16
235 #else
236         /* Copy source to destination */
237         call    copy_bytes
238 #endif
239
240         /* Zero .bss portion */
241         negl    %ecx
242         addl    %edx, %ecx
243         pushw   %ax
244         xorw    %ax, %ax
245         rep addr32 stosb
246         popw    %ax
247
248         /* Round up %esi to start of next source block */
249         addl    $0xf, %esi
250         andl    $~0xf, %esi
251
252         ret
253         .size install_block, . - install_block
254         
255 /****************************************************************************
256  * alloc_basemem (real-mode near call)
257  *
258  * Allocate space for .text16 and .data16 from top of base memory.
259  * Memory is allocated using the BIOS free base memory counter at
260  * 0x40:13.
261  *
262  * Parameters: 
263  *   none
264  * Returns:
265  *   %ax : .text16 segment address
266  *   %bx : .data16 segment address
267  * Corrupts:
268  *   none
269  ****************************************************************************
270  */
271         .section ".prefix.lib"
272         .code16
273 alloc_basemem:
274         /* FBMS => %ax as segment address */
275         movw    $0x40, %ax
276         movw    %ax, %fs
277         movw    %fs:0x13, %ax
278         shlw    $6, %ax
279
280         /* .data16 segment address */
281         subw    $_data16_size_pgh, %ax
282         pushw   %ax
283
284         /* .text16 segment address */
285         subw    $_text16_size_pgh, %ax
286         pushw   %ax
287
288         /* Update FBMS */
289         shrw    $6, %ax
290         movw    %ax, %fs:0x13
291
292         /* Return */
293         popw    %ax
294         popw    %bx
295         ret
296         .size alloc_basemem, . - alloc_basemem
297
298 /****************************************************************************
299  * install_basemem (real-mode near call)
300  *
301  * Install .text16 and .data16 into base memory
302  *
303  * Parameters: 
304  *   %ax : .text16 segment address
305  *   %bx : .data16 segment address
306  *   %esi : source physical address (must be a multiple of 16)
307  * Returns:
308  *   %esi : next source physical address (will be a multiple of 16)
309  * Corrupts:
310  *   none
311  ****************************************************************************
312  */
313         .section ".prefix.lib"
314         .code16
315 install_basemem:
316         /* Preserve registers */
317         pushw   %ds
318         pushw   %es
319         pushl   %edi
320         pushl   %ecx
321         pushl   %edx
322
323         /* Install .text16 */
324         pushl   %esi
325         shrl    $4, %esi
326         movw    %si, %ds
327         xorw    %si, %si
328         movw    %ax, %es
329         xorl    %edi, %edi
330         movl    $_text16_size, %ecx
331         movl    %ecx, %edx
332         call    install_block
333         popl    %ecx
334         addl    %ecx, %esi
335
336         /* Install .data16 */
337         pushl   %esi
338         shrl    $4, %esi
339         movw    %si, %ds
340         xorw    %si, %si
341         movw    %bx, %es
342         xorl    %edi, %edi
343         movl    $_data16_progbits_size, %ecx
344         movl    $_data16_size, %edx
345         call    install_block
346         popl    %ecx
347         addl    %ecx, %esi
348
349         /* Restore registers */
350         popl    %edx
351         popl    %ecx
352         popl    %edi
353         popw    %es
354         popw    %ds
355         ret
356         .size install_basemem, . - install_basemem
357
358 /****************************************************************************
359  * install_highmem (real-mode near call)
360  *
361  * Install .text and .data into high memory
362  *
363  * Parameters:
364  *   %esi : source physical address (must be a multiple of 16)
365  *   %edi : destination physical address
366  * Returns:
367  *   %esi : next source physical address (will be a multiple of 16)
368  * Corrupts:
369  *   none
370  ****************************************************************************
371  */
372
373 #ifndef KEEP_IT_REAL
374
375         .section ".prefix.lib"
376         .code16
377 install_highmem:
378         /* Preserve registers */
379         pushw   %ax
380         pushl   %edi
381         pushl   %ecx
382         pushl   %edx
383
384         /* Install .text and .data to specified address */
385         movl    $_textdata_progbits_size, %ecx
386         movl    $_textdata_size, %edx
387         movw    $install_block, %ax
388         call    pm_call
389
390         /* Restore registers */
391         popl    %edx
392         popl    %ecx
393         popl    %edi
394         popw    %ax
395         ret
396         .size install_highmem, . - install_highmem
397         
398 #endif /* KEEP_IT_REAL */
399         
400 /****************************************************************************
401  * install (real-mode near call)
402  * install_prealloc (real-mode near call)
403  *
404  * Install all text and data segments.
405  *
406  * Parameters:
407  *   %ax : .text16 segment address (install_prealloc only)
408  *   %bx : .data16 segment address (install_prealloc only)
409  * Returns:
410  *   %ax : .text16 segment address
411  *   %bx : .data16 segment address
412  *   %edi : .text physical address (if applicable)
413  * Corrupts:
414  *   none
415  ****************************************************************************
416  */
417         .section ".prefix.lib"
418         .code16
419         .globl install
420 install:
421         /* Allocate space for .text16 and .data16 */
422         call    alloc_basemem
423         .size install, . - install
424         .globl install_prealloc
425 install_prealloc:
426         /* Save registers */
427         pushw   %ds
428         pushl   %esi
429
430         /* Sanity: clear the direction flag asap */
431         cld
432
433         /* Calculate physical address of payload (i.e. first source) */
434         xorl    %esi, %esi
435         movw    %cs, %si
436         shll    $4, %esi
437         addl    $_payload_offset, %esi
438
439         /* Install .text16 and .data16 */
440         call    install_basemem
441
442         /* Set up %ds for access to .data16 */
443         movw    %bx, %ds
444
445 #ifdef KEEP_IT_REAL
446         /* Initialise libkir */
447         movw    %ax, (init_libkir_vector+2)
448         lcall   *init_libkir_vector
449 #else
450         /* Save registers */
451         pushl   %edi
452         pushl   %ecx
453
454         /* Install .text and .data to temporary area in high memory,
455          * prior to reading the E820 memory map and relocating
456          * properly.
457          */
458         movl    $HIGHMEM_LOADPOINT, %edi
459         call    install_highmem
460
461         /* Initialise librm at current location */
462         movw    %ax, (init_librm_vector+2)
463         lcall   *init_librm_vector
464
465         /* Call relocate() to determine target address for relocation.
466          * relocate() will return with %esi, %edi and %ecx set up
467          * ready for the copy to the new location.
468          */
469         movw    %ax, (prot_call_vector+2)
470         pushl   $relocate
471         lcall   *prot_call_vector
472         addw    $4, %sp
473
474         /* Copy code to new location */
475         pushl   %edi
476         pushw   %ax
477         movw    $copy_bytes, %ax
478         call    pm_call
479         popw    %ax
480         popl    %edi
481
482         /* Initialise librm at new location */
483         lcall   *init_librm_vector
484
485         /* Restore registers */
486         popl    %ecx
487         popl    %edi
488 #endif
489         popl    %esi
490         popw    %ds
491         ret
492         .size install_prealloc, . - install_prealloc
493
494         /* Vectors for far calls to .text16 functions */
495         .section ".data16"
496 #ifdef KEEP_IT_REAL
497 init_libkir_vector:
498         .word init_libkir
499         .word 0
500         .size init_libkir_vector, . - init_libkir_vector
501 #else
502 init_librm_vector:
503         .word init_librm
504         .word 0
505         .size init_librm_vector, . - init_librm_vector
506 prot_call_vector:
507         .word prot_call
508         .word 0
509         .size prot_call_vector, . - prot_call_vector
510 #endif
511
512
513         /* File split information for the compressor */
514 #if COMPRESS
515         .section ".zinfo", "a"
516         .ascii  "COPY"
517         .long   _prefix_load_offset
518         .long   _prefix_progbits_size
519         .long   _max_align
520         .ascii  "PACK"
521         .long   _text16_load_offset
522         .long   _text16_progbits_size
523         .long   _max_align
524         .ascii  "PACK"
525         .long   _data16_load_offset
526         .long   _data16_progbits_size
527         .long   _max_align
528         .ascii  "PACK"
529         .long   _textdata_load_offset
530         .long   _textdata_progbits_size
531         .long   _max_align
532 #else /* COMPRESS */
533         .section ".zinfo", "a"
534         .ascii  "COPY"
535         .long   _prefix_load_offset
536         .long   _load_size
537         .long   _max_align
538 #endif /* COMPRESS */