Add infrastructure to support access to .data16 (and .text16) variables
[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 #define CR0_PE 1
21
22         .arch i386
23         .section ".prefix.lib", "awx", @progbits
24
25 /****************************************************************************
26  * install_block (real-mode near call)
27  *
28  * Install block to specified address
29  *
30  * Parameters:
31  *   %esi : byte offset within loaded image (must be a multiple of 16)
32  *   %es:edi : destination address
33  *   %ecx : length of (decompressed) data
34  *   %edx : total length of block (including any uninitialised data portion)
35  * Returns:
36  *   none
37  * Corrupts:
38  *   %esi, %edi, %ecx, %edx
39  ****************************************************************************
40  */
41         .section ".prefix.lib"
42         .code16
43 install_block:
44         /* Preserve registers */
45         pushw   %ds
46         pushl   %eax
47         
48         /* Starting segment => %ds */
49         movw    %cs, %ax
50         shrl    $4, %esi
51         addw    %si, %ax
52         movw    %ax, %ds
53         xorl    %esi, %esi
54
55         /* Calculate start and length of uninitialised data portion */
56         leal    (%edi,%ecx), %eax
57         subl    %ecx, %edx
58         
59         /* Do the copy */
60         cld
61         rep addr32 movsb /* or "call decompress16" */
62
63         /* Zero remaining space */
64         movl    %eax, %edi
65         movl    %edx, %ecx
66         xorb    %al, %al
67         rep addr32 stosb
68
69         /* Restore registers */
70         popl    %eax
71         popw    %ds
72         ret
73         .size install_block, . - install_block
74         
75 /****************************************************************************
76  * alloc_basemem (real-mode near call)
77  *
78  * Allocate space for .text16 and .data16 from top of base memory.
79  * Memory is allocated using the BIOS free base memory counter at
80  * 0x40:13.
81  *
82  * Parameters: 
83  *   none
84  * Returns:
85  *   %ax : .text16 segment address
86  *   %bx : .data16 segment address
87  * Corrupts:
88  *   none
89  ****************************************************************************
90  */
91         .section ".prefix.lib"
92         .code16
93 alloc_basemem:
94         /* FBMS => %ax as segment address */
95         movw    $0x40, %ax
96         movw    %ax, %fs
97         movw    %fs:0x13, %ax
98         shlw    $6, %ax
99
100         /* .data16 segment address */
101         subw    $_data16_size, %ax
102         pushw   %ax
103
104         /* .text16 segment address */
105         subw    $_text16_size, %ax
106         pushw   %ax
107
108         /* Update FBMS */
109         shrw    $6, %ax
110         movw    %ax, %fs:0x13
111
112         /* Return */
113         popw    %ax
114         popw    %bx
115         ret
116         .size alloc_basemem, . - alloc_basemem
117
118 /****************************************************************************
119  * install_basemem (real-mode near call)
120  *
121  * Install .text16 and .data16 into base memory
122  *
123  * Parameters: 
124  *   %ax : .text16 segment address
125  *   %bx : .data16 segment address
126  * Returns:
127  *   none
128  * Corrupts:
129  *   none
130  ****************************************************************************
131  */
132         .section ".prefix.lib"
133         .code16
134 install_basemem:
135         /* Preserve registers */
136         pushw   %es
137         pushl   %esi
138         pushl   %edi
139         pushl   %ecx
140         pushl   %edx
141
142         /* Install .text16 */
143         movw    %ax, %es
144         xorl    %edi, %edi
145         movl    $_text16_load_offset, %esi
146         movl    $_text16_size, %ecx
147         movl    %ecx, %edx
148         call    install_block
149
150         /* Install .data16 */
151         movw    %bx, %es
152         xorl    %edi, %edi      
153         movl    $_data16_load_offset, %esi
154         movl    $_data16_progbits_size, %ecx
155         movl    $_data16_size, %edx
156         call    install_block
157
158         /* Restore registers */
159         popl    %edx
160         popl    %ecx
161         popl    %edi
162         popl    %esi
163         popw    %es
164         ret
165         .size install_basemem, . - install_basemem
166
167 /****************************************************************************
168  * GDT for flat real mode
169  *
170  * We only ever use this GDT to set segment limits; the bases are
171  * unused.  Also, we only flatten data segments, so we don't need to
172  * worry about the code or stack segments.  This makes everything much
173  * simpler.
174  ****************************************************************************
175  */
176         
177 #ifndef KEEP_IT_REAL
178         
179         .section ".prefix.lib"
180         .align 16
181 gdt:
182 gdt_limit:              .word gdt_length - 1
183 gdt_base:               .long 0
184                         .word 0 /* padding */
185
186 real_ds:        /* Genuine real mode data segment */
187         .equ    REAL_DS, real_ds - gdt
188         .word   0xffff, 0
189         .byte   0, 0x93, 0, 0
190
191 flat_ds:        /* Flat real mode data segment */
192         .equ    FLAT_DS, flat_ds - gdt
193         .word   0xffff, 0
194         .byte   0, 0x93, 0xcf, 0
195
196 gdt_end:
197         .equ    gdt_length, gdt_end - gdt
198         .size gdt, . - gdt
199         
200 #endif /* KEEP_IT_REAL */
201
202 /****************************************************************************
203  * set_segment_limits (real-mode near call)
204  *
205  * Sets limits on the data segments %ds and %es.
206  *
207  * Parameters: 
208  *   %cx : Segment limit ($REAL_DS or $FLAT_DS)
209  ****************************************************************************
210  */
211
212 #ifndef KEEP_IT_REAL
213         
214         .section ".prefix.lib"
215         .code16
216 set_segment_limits:
217         /* Preserve real-mode segment values and temporary registers */
218         pushw   %es
219         pushw   %ds
220         pushl   %eax
221
222         /* Set GDT base and load GDT */
223         xorl    %eax, %eax
224         movw    %cs, %ax
225         shll    $4, %eax
226         addl    $gdt, %eax
227         movl    %eax, %cs:gdt_base
228         lgdt    %cs:gdt
229
230         /* Switch to protected mode, set segment limits, switch back */
231         movl    %cr0, %eax
232         orb     $CR0_PE, %al
233         movl    %eax, %cr0
234         movw    %cx, %ds
235         movw    %cx, %es
236         andb    $0!CR0_PE, %al
237         movl    %eax, %cr0
238
239         /* Restore real-mode segment values and temporary registers */
240         popl    %eax
241         popw    %ds
242         popw    %es
243         ret
244         .size set_segment_limits, . - set_segment_limits
245         
246 #endif /* KEEP_IT_REAL */
247         
248 /****************************************************************************
249  * install_highmem (real-mode near call)
250  *
251  * Install .text and .data into high memory
252  *
253  * Parameters:
254  *   %edi : physical address in high memory
255  * Returns:
256  *   none
257  * Corrupts:
258  *   none
259  ****************************************************************************
260  */
261
262 #ifndef KEEP_IT_REAL
263
264         .section ".prefix.lib"
265         .code16
266 install_highmem:
267         /* Preserve registers and interrupt status */
268         pushfl
269         pushl   %esi
270         pushl   %edi
271         pushl   %ecx
272         pushl   %edx
273                 
274         /* Disable interrupts and flatten real mode */
275         cli
276         movw    $FLAT_DS, %cx
277         call    set_segment_limits
278
279         /* Install .text and .data to specified address */
280         xorw    %cx, %cx
281         movw    %cx, %es
282         movl    $_textdata_load_offset, %esi
283         movl    $_textdata_progbits_size, %ecx
284         movl    $_textdata_size, %edx
285         call    install_block
286
287         /* Unflatten real mode */
288         movw    $REAL_DS, %cx
289         call    set_segment_limits
290
291         /* Restore registers and interrupt status */
292         popl    %edx
293         popl    %ecx
294         popl    %edi
295         popl    %esi
296         popfl
297         ret
298         .size install_highmem, . - install_highmem
299         
300 #endif /* KEEP_IT_REAL */
301         
302 /****************************************************************************
303  * install (real-mode near call)
304  * install_prealloc (real-mode near call)
305  *
306  * Install all text and data segments.
307  *
308  * Parameters:
309  *   %ax : .text16 segment address (install_prealloc only)
310  *   %bx : .data16 segment address (install_prealloc only)
311  * Returns:
312  *   %ax : .text16 segment address
313  *   %bx : .data16 segment address
314  *   %edi : .text physical address (if applicable)
315  * Corrupts:
316  *   none
317  ****************************************************************************
318  */
319         .section ".prefix.lib"
320         .code16
321         .globl install
322 install:
323         /* Allocate space for .text16 and .data16 */
324         call    alloc_basemem
325         .size install, . - install
326         .globl install_prealloc
327 install_prealloc:
328         /* Install .text16 and .data16 */
329         call    install_basemem
330
331 #ifndef KEEP_IT_REAL
332         /* Install .text and .data to 2MB mark.  Use 2MB to avoid
333          * problems with A20.
334          */
335         movl    $(2<<20), %edi
336         call    install_highmem
337
338         /* Continue executing in .text16 segment */
339         movw    %bx, %ds
340         pushw   %cs
341         pushw   $2f
342         pushw   %ax
343         pushw   $1f
344         lret
345         .section ".text16", "awx", @progbits
346 1:
347         /* Set up protected-mode GDT, call relocate(), reset GDT */
348         call    init_librm
349         pushl   $relocate
350         data32 call     prot_call
351         addw    $4, %sp
352         call    init_librm
353
354         /* Return to executing in .prefix section */
355         lret
356         .section ".prefix.lib"
357 2:
358 #endif
359         ret
360         .size install_prealloc, . - install_prealloc