9349cf2b19a1944a798b185e22ad93d2a3e2f43a
[people/xl0/gpxe.git] / src / arch / i386 / firmware / pcbios / e820mangler.S
1 #undef CODE16
2 #if defined(PCBIOS)
3 #define CODE16
4 #endif
5
6 #ifdef CODE16
7         
8 #define BOCHSBP xchgw %bx,%bx
9         
10         .text
11         .arch i386
12         .section ".text16", "ax", @progbits
13         .code16
14
15 /****************************************************************************
16  * Memory map mangling code
17  ****************************************************************************
18  */
19
20         .globl  e820mangler
21 e820mangler:
22
23 /* Macro to calculate offset of labels within code segment in
24  * installed copy of code.
25  */
26 #define INSTALLED(x) ( (x) - e820mangler )
27         
28 /****************************************************************************
29  * Intercept INT 15 memory calls and remove the hidden memory ranges
30  * from the resulting memory map.
31  ****************************************************************************
32  */
33         .globl  _intercept_int15
34 _intercept_int15:
35         /* Preserve registers */
36         pushw   %bp
37         /* Store %ax for future reference */
38         pushw   %ax
39         /* Make INT-style call to old INT15 routine */
40         pushfw
41         lcall   %cs:*INSTALLED(_intercepted_int15)
42         /* Preserve flags returned by original E820 routine */
43         pushfw
44         /* Check for valid INT15 routine */
45         jc      intercept_int15_exit
46         /* Check for a routine we want to intercept */
47         movw    %sp, %bp
48         cmpw    $0xe820, 2(%bp)
49         je      intercept_e820
50         cmpw    $0xe801, 2(%bp)
51         je      intercept_e801
52         cmpb    $0x88, 3(%bp)
53         je      intercept_88
54 intercept_int15_exit:
55         /* Restore registers and return */
56         popfw
57         popw    %bp                     /* discard original %ax */
58         popw    %bp
59         lret    $2                      /* 'iret' - flags already loaded */
60
61         .globl  _intercepted_int15
62 _intercepted_int15:     .word 0,0
63                 
64 /****************************************************************************
65  * Exclude an address range from a potentially overlapping address range
66  *
67  * Note: this *can* be called even if the range doesn't overlap; it
68  * will simply return the range unaltered.  It copes with all the
69  * possible cases of overlap, including total overlap (which will
70  * modify the range to length zero).  If the to-be-excluded range is
71  * in the middle of the target range, then the larger remaining
72  * portion will be returned.  If %di is nonzero on entry then the
73  * range will only be truncated from the high end, i.e. the base
74  * address will never be altered.  All this in less than 30
75  * instructions.  :)
76  *
77  * Parameters:
78  *  %eax        Base address of memory range
79  *  %ecx        Length of memory range
80  *  %ebx        Base address of memory range to exclude
81  *  %edx        Length of memory range to exclude
82  *  %di         0 => truncate either end, 1 => truncate high end only
83  * Returns:
84  *  %eax        Updated base address of range
85  *  %ecx        Updated length of range
86  *  %ebx,%edx   Undefined
87  *              All other registers (including %di) preserved
88  *
89  * Note: "ja" is used rather than "jg" because we are comparing
90  * unsigned ints
91  ****************************************************************************
92  */
93 #ifdef TEST_EXCLUDE_ALGORITHM
94         .code32
95 #endif /* TEST_EXCLUDE_ALGORITHM */
96 exclude_memory_range:
97         /* Convert (start,length) to (start,end) */
98         addl    %eax, %ecx
99         addl    %ebx, %edx
100         /* Calculate "prefix" length */
101         subl    %eax, %ebx              /* %ebx = "prefix" length */
102         ja      1f
103         xorl    %ebx, %ebx              /* Truncate to zero if negative */
104 1:      /* %di == 0 => truncate either end
105          * %di != 0 => truncate only high end
106          */
107         testw   %di, %di
108         je      use_either
109         cmpl    %eax, %edx
110         jbe     99f                     /* excl. range is below target range */
111 use_prefix:     /* Use prefix, discard suffix */
112         addl    %eax, %ebx              /* %ebx = candidate end address */
113         cmpl    %ecx, %ebx              /* %ecx = min ( %ebx, %ecx ) */
114         ja      1f
115         movl    %ebx, %ecx
116 1:      jmp     99f
117 use_either:             
118         /* Calculate "suffix" length */
119         subl    %ecx, %edx              /* %edx = -( "suffix" length ) */
120         jb      1f
121         xorl    %edx, %edx              /* Truncate to zero if negative */
122 1:      negl    %edx                    /* %edx = "suffix" length */
123         /* Use whichever is longest of "prefix" and "suffix" */
124         cmpl    %ebx, %edx
125         jbe     use_prefix
126 use_suffix:     /* Use suffix, discard prefix */
127         negl    %edx
128         addl    %ecx, %edx              /* %edx = candidate start address */
129         cmpl    %eax, %edx              /* %eax = max ( %eax, %edx ) */
130         jb      1f
131         movl    %edx, %eax
132 1:      
133 99:     subl    %eax, %ecx              /* Convert back to (start,length) */
134         ret
135
136 #ifdef TEST_EXCLUDE_ALGORITHM
137         .globl  __test_exclude
138 __test_exclude:
139         pushl   %ebx
140         pushl   %edi
141         movl    12(%esp), %eax
142         movl    16(%esp), %ecx
143         movl    20(%esp), %ebx
144         movl    24(%esp), %edx
145         movl    28(%esp), %edi
146         call    exclude_memory_range
147         shll    $16, %eax
148         orl     %ecx, %eax
149         popl    %edi
150         popl    %ebx
151         ret
152         .code16
153 #endif /* TEST_EXCLUDE_ALGORITHM */
154         
155 /****************************************************************************
156  * Exclude Etherboot-reserved address ranges from a potentially
157  * overlapping address range
158  *
159  * Parameters:
160  *  %eax        Base address of memory range
161  *  %ecx        Length of memory range
162  *  %di         0 => truncate either end, 1 => truncate high end only
163  * Returns:
164  *  %eax        Updated base address of range
165  *  %ecx        Updated length of range
166  *              All other registers (including %di) preserved
167  ****************************************************************************
168  */
169 exclude_hidden_memory_ranges:
170         pushw   %si
171         pushl   %ebx
172         pushl   %edx
173         movw    $INSTALLED(_hide_memory), %si
174 2:      movl    %cs:0(%si), %ebx
175         movl    %cs:4(%si), %edx
176         call    exclude_memory_range
177         addw    $8, %si
178         cmpw    $INSTALLED(_hide_memory_end), %si
179         jl      2b
180         popl    %edx
181         popl    %ebx
182         popw    %si
183         ret
184         
185         .globl  _hide_memory    
186 _hide_memory:
187         .long   0,0                     /* Etherboot text (base,length) */
188         .long   0,0                     /* Heap (base,length) */
189 _hide_memory_end:
190
191 /****************************************************************************
192  * Intercept INT 15,E820 calls and remove the hidden memory ranges
193  * from the resulting memory map.
194  ****************************************************************************
195  */
196 #define SMAP ( 0x534d4150 )
197 intercept_e820:
198         /* Check for valid E820 routine */
199         cmpl    $SMAP, %eax
200         jne     intercept_int15_exit
201         /* If base address isn't in the low 4GB, return unaltered
202          * (since we never claim memory above 4GB).  WARNING: we cheat
203          * by assuming that no E820 region will straddle the 4GB
204          * boundary: if this is not a valid assumption then things
205          * will probably break.
206          */
207         cmpl    $0, %es:4(%di)
208         jne     intercept_int15_exit
209         /* Preserve registers */
210         pushl   %eax
211         pushl   %ecx
212         /* Update returned memory range */
213         movl    %es:0(%di), %eax        /* Base */
214         movl    %es:8(%di), %ecx        /* Length */
215         pushw   %di
216         xorw    %di, %di                /* "truncate either end" flag */
217         call    exclude_hidden_memory_ranges
218         popw    %di
219         movl    %eax, %es:0(%di)        /* Store updated base */
220         movl    %ecx, %es:8(%di)        /* Store updated length */
221         /* Restore registers and return */
222         popl    %ecx
223         popl    %eax
224         jmp     intercept_int15_exit
225
226 /****************************************************************************
227  * Intercept INT 15,E801 calls and remove the hidden memory ranges
228  * from the resulting memory map.
229  ****************************************************************************
230  */
231 intercept_e801:
232         /* Adjust return values */
233         call    e801_adjust
234         xchgw   %ax, %cx
235         xchgw   %bx, %dx
236         call    e801_adjust
237         xchgw   %ax, %cx
238         xchgw   %bx, %dx
239         jmp     intercept_int15_exit
240         
241         /* %ax = #KB from 1MB+, %bx = #64KB from 16MB+
242          * Return with modified values in %ax, %bx.  Preserver other regs.
243          */
244 e801_adjust:
245         pushw   %di
246         pushl   %ecx
247         pushl   %eax
248         movw    $1, %di                 /* "truncate only high end" flag */
249
250         /* Truncate #64KB from 16MB+ as appropriate */
251         movw    %bx, %cx                /* (no need to zero high word) */
252         shll    $16, %ecx               /* %ecx = length in bytes */
253         movl    $(1<<24), %eax          /* 16MB start address */
254         call    exclude_hidden_memory_ranges
255         shrl    $16, %ecx               /* %cx = updated length in 64KB */
256         movw    %cx, %bx                /* Return in %bx */
257         
258         /* Truncate #KB from 1MB+ as appropriate */
259         popw    %cx                     /* Orig. %ax (high word already 0) */
260         shll    $10, %ecx               /* %ecx = length in bytes */
261         shrl    $4, %eax                /* 1MB start address */
262         call    exclude_hidden_memory_ranges
263         shrl    $10, %ecx               /* %cx = updated length in KB */
264         pushw   %cx                     /* Will be picked up in %eax */
265         
266         popl    %eax
267         popl    %ecx
268         popw    %di
269         ret
270
271 /****************************************************************************
272  * Intercept INT 15,88 calls and remove the hidden memory ranges
273  * from the resulting memory map.
274  ****************************************************************************
275  */
276 intercept_88:
277         pushw   %bx                     /* E801 adjust, ignore %bx */
278         call    e801_adjust
279         popw    %bx
280         jmp     intercept_int15_exit
281
282         .globl  e820mangler_end
283 e820mangler_end:
284
285         .globl  _e820mangler_size
286         .equ    _e820mangler_size, e820mangler_end - e820mangler
287         .globl  e820mangler_size
288 e820mangler_size:
289         .word   _e820mangler_size
290
291 #else
292
293         .globl  _e820mangler_size
294         .equ    _e820mangler_size, 0
295         
296 #endif /* CODE16 */