Ensure that empty e820 regions are skipped even at the end of the
[people/xl0/gpxe.git] / src / arch / i386 / firmware / pcbios / e820mangler.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         .text
20         .arch i386
21         .section ".text16", "ax", @progbits
22         .section ".data16", "aw", @progbits
23         .section ".text16.data", "aw", @progbits
24         .code16
25
26 #define SMAP 0x534d4150
27
28 /****************************************************************************
29  * Check for overlap
30  *
31  * Parameters:
32  *  %edx:%eax   Region start
33  *  %ecx:%ebx   Region end
34  *  %si         Pointer to hidden region descriptor
35  * Returns:
36  *  CF set      Region overlaps
37  *  CF clear    No overlap
38  ****************************************************************************
39  */ 
40         .section ".text16"
41 check_overlap:
42         /* If start >= hidden_end, there is no overlap. */
43         testl   %edx, %edx
44         jnz     no_overlap
45         cmpl    4(%si), %eax
46         jae     no_overlap
47         /* If end <= hidden_start, there is no overlap; equivalently,
48          * if end > hidden_start, there is overlap.
49         */
50         testl   %ecx, %ecx
51         jnz     overlap
52         cmpl    0(%si), %ebx
53         ja      overlap
54 no_overlap:
55         clc
56         ret
57 overlap:
58         stc
59         ret
60         .size check_overlap, . - check_overlap
61
62 /****************************************************************************
63  * Check for overflow/underflow
64  *
65  * Parameters:
66  *  %edx:%eax   Region start
67  *  %ecx:%ebx   Region end
68  * Returns:
69  *  CF set      start < end
70  *  CF clear    start >= end
71  ****************************************************************************
72  */
73         .section ".text16"
74 check_overflow:
75         pushl   %ecx
76         pushl   %ebx
77         subl    %eax, %ebx
78         sbbl    %edx, %ecx
79         popl    %ebx
80         popl    %ecx
81         ret
82         .size check_overflow, . - check_overflow
83         
84 /****************************************************************************
85  * Truncate towards start of region
86  *
87  * Parameters:
88  *  %edx:%eax   Region start
89  *  %ecx:%ebx   Region end
90  *  %si         Pointer to hidden region descriptor
91  * Returns:
92  *  %edx:%eax   Modified region start
93  *  %ecx:%ebx   Modified region end
94  *  CF set      Region was truncated
95  *  CF clear    Region was not truncated
96  ****************************************************************************
97  */
98         .section ".text16"
99 truncate_to_start:
100         /* If overlaps, set region end = hidden region start */
101         call    check_overlap
102         jnc     99f
103         movl    0(%si), %ebx
104         xorl    %ecx, %ecx
105         /* If region end < region start, set region end = region start */
106         call    check_overflow
107         jnc     1f
108         movl    %eax, %ebx
109         movl    %edx, %ecx
110 1:      stc
111 99:     ret
112         .size truncate_to_start, . - truncate_to_start
113
114 /****************************************************************************
115  * Truncate towards end of region
116  *
117  * Parameters:
118  *  %edx:%eax   Region start
119  *  %ecx:%ebx   Region end
120  *  %si         Pointer to hidden region descriptor
121  * Returns:
122  *  %edx:%eax   Modified region start
123  *  %ecx:%ebx   Modified region end
124  *  CF set      Region was truncated
125  *  CF clear    Region was not truncated
126  ****************************************************************************
127  */
128         .section ".text16"
129 truncate_to_end:
130         /* If overlaps, set region start = hidden region end */
131         call    check_overlap
132         jnc     99f
133         movl    4(%si), %eax
134         xorl    %edx, %edx
135         /* If region start > region end, set region start = region end */
136         call    check_overflow
137         jnc     1f
138         movl    %ebx, %eax
139         movl    %ecx, %edx
140 1:      stc
141 99:     ret
142         .size truncate_to_end, . - truncate_to_end
143         
144 /****************************************************************************
145  * Truncate region
146  *
147  * Parameters:
148  *  %edx:%eax   Region start
149  *  %ecx:%ebx   Region length (*not* region end)
150  *  %bp         truncate_to_start or truncate_to_end
151  * Returns:
152  *  %edx:%eax   Modified region start
153  *  %ecx:%ebx   Modified region length
154  *  CF set      Region was truncated
155  *  CF clear    Region was not truncated
156  ****************************************************************************
157  */
158         .section ".text16"
159 truncate:
160         pushw   %si
161         pushfw
162         /* Convert (start,len) to (start,end) */
163         addl    %eax, %ebx
164         adcl    %edx, %ecx
165         /* Hide all hidden regions, truncating as directed */
166         movw    $hidden_regions, %si
167 1:      call    *%bp
168         jnc     2f
169         popfw   /* If CF was set, set stored CF in flags word on stack */
170         stc
171         pushfw
172 2:      addw    $8, %si
173         cmpl    $0, 0(%si)
174         jne     1b
175         /* Convert modified (start,end) back to (start,len) */
176         subl    %eax, %ebx
177         sbbl    %edx, %ecx
178         popfw
179         popw    %si
180         ret
181         .size truncate, . - truncate
182
183 /****************************************************************************
184  * Patch "memory above 1MB" figure
185  *
186  * Parameters:
187  *  %ax         Memory above 1MB, in 1kB blocks
188  * Returns:
189  *  %ax         Modified memory above 1M in 1kB blocks
190  *  CF set      Region was truncated
191  *  CF clear    Region was not truncated
192  ****************************************************************************
193  */
194         .section ".text16"
195 patch_1m:
196         pushal
197         /* Convert to (start,len) format and call truncate */
198         movw    $truncate_to_start, %bp
199         xorl    %ecx, %ecx
200         movzwl  %ax, %ebx
201         shll    $10, %ebx
202         xorl    %edx, %edx
203         movl    $0x100000, %eax
204         call    truncate
205         /* Convert back to "memory above 1MB" format and return via %ax */
206         pushfw
207         shrl    $10, %ebx
208         popfw
209         movw    %sp, %bp
210         movw    %bx, 28(%bp)
211         popal
212         ret
213         .size patch_1m, . - patch_1m
214
215 /****************************************************************************
216  * Patch "memory above 16MB" figure
217  *
218  * Parameters:
219  *  %bx         Memory above 16MB, in 64kB blocks
220  * Returns:
221  *  %bx         Modified memory above 16M in 64kB blocks
222  *  CF set      Region was truncated
223  *  CF clear    Region was not truncated
224  ****************************************************************************
225  */
226         .section ".text16"
227 patch_16m:
228         pushal
229         /* Convert to (start,len) format and call truncate */
230         movw    $truncate_to_start, %bp
231         xorl    %ecx, %ecx
232         shll    $16, %ebx
233         xorl    %edx, %edx
234         movl    $0x1000000, %eax
235         call    truncate
236         /* Convert back to "memory above 16MB" format and return via %bx */
237         pushfw
238         shrl    $16, %ebx
239         popfw
240         movw    %sp, %bp
241         movw    %bx, 16(%bp)
242         popal
243         ret
244         .size patch_16m, . - patch_16m
245
246 /****************************************************************************
247  * Patch "memory between 1MB and 16MB" and "memory above 16MB" figures
248  *
249  * Parameters:
250  *  %ax         Memory between 1MB and 16MB, in 1kB blocks
251  *  %bx         Memory above 16MB, in 64kB blocks
252  * Returns:
253  *  %ax         Modified memory between 1MB and 16MB, in 1kB blocks
254  *  %bx         Modified memory above 16MB, in 64kB blocks
255  *  CF set      Region was truncated
256  *  CF clear    Region was not truncated
257  ****************************************************************************
258  */
259         .section ".text16"
260 patch_1m_16m:
261         call    patch_1m
262         jc      1f
263         call    patch_16m
264         ret
265 1:      /* 1m region was truncated; kill the 16m region */
266         xorw    %bx, %bx
267         ret
268         .size patch_1m_16m, . - patch_1m_16m
269
270 /****************************************************************************
271  * Patch E820 memory map entry
272  *
273  * Parameters:
274  *  %es:di      Pointer to E820 memory map descriptor
275  *  %bp         truncate_to_start or truncate_to_end
276  * Returns:
277  *  %es:di      Pointer to now-modified E820 memory map descriptor
278  *  CF set      Region was truncated
279  *  CF clear    Region was not truncated
280  ****************************************************************************
281  */
282         .section ".text16"
283 patch_e820:
284         pushal
285         movl    %es:0(%di), %eax
286         movl    %es:4(%di), %edx
287         movl    %es:8(%di), %ebx
288         movl    %es:12(%di), %ecx
289         call    truncate
290         movl    %eax, %es:0(%di)
291         movl    %edx, %es:4(%di)
292         movl    %ebx, %es:8(%di)
293         movl    %ecx, %es:12(%di)
294         popal
295         ret
296         .size patch_e820, . - patch_e820
297
298 /****************************************************************************
299  * Split E820 memory map entry if necessary
300  *
301  * Parameters:
302  *   As for INT 15,e820
303  * Returns:
304  *   As for INT 15,e820
305  *
306  * Calls the underlying INT 15,e820 and returns a modified memory map.
307  * Regions will be split around any hidden regions.
308  ****************************************************************************
309  */
310         .section ".text16"
311 split_e820:
312         pushw   %si
313         pushw   %bp
314         /* Caller's %bx => %si, real %ebx to %ebx, call previous handler */
315         pushfw
316         movw    %bx, %si
317         testl   %ebx, %ebx
318         jnz     1f
319         movl    %ebx, %cs:real_ebx
320 1:      movl    %cs:real_ebx, %ebx
321         lcall   *%cs:int15_vector
322         pushfw
323         /* Edit result */
324         pushw   %ds
325         pushw   %cs:rm_ds
326         popw    %ds
327         movw    $truncate_to_start, %bp
328         incw    %si
329         jns     2f
330         movw    $truncate_to_end, %bp
331 2:      call    patch_e820
332         jnc     3f
333         xorw    $0x8000, %si
334 3:      testw   %si, %si
335         js      4f
336         movl    %ebx, %cs:real_ebx
337         testl   %ebx, %ebx
338         jz      5f
339 4:      movw    %si, %bx
340 5:      popw    %ds
341         /* Restore flags returned by previous handler and return */
342         popfw
343         popw    %bp
344         popw    %si
345         ret
346         .size split_e820, . - split_e820
347
348         .section ".text16.data"
349 real_ebx:
350         .long 0
351         .size real_ebx, . - real_ebx
352
353 /****************************************************************************
354  * INT 15,e820 handler
355  ****************************************************************************
356  */
357         .section ".text16"
358 int15_e820:
359         pushl   %eax
360         pushl   %ecx
361         pushl   %edx    
362         call    split_e820
363         pushfw
364         /* If we've hit an error, exit immediately */
365         jc      99f
366         /* If region is non-empty, return this region */
367         pushl   %eax
368         movl    %es:8(%di), %eax
369         orl     %es:12(%di), %eax
370         popl    %eax
371         jnz     99f
372         /* Region is empty.  If this is not the end of the map,
373          * skip over this region.
374          */
375         testl   %ebx, %ebx
376         jz      1f
377         popfw
378         popl    %edx
379         popl    %ecx
380         popl    %eax
381         jmp     int15_e820
382 1:      /* Region is empty and this is the end of the map.  Return
383          * with CF set to avoid placing an empty region at the end of
384          * the map.
385          */
386         popfw
387         stc
388         pushfw
389 99:     /* Restore flags from original INT 15,e820 call and return */
390         popfw
391         addr32 leal     12(%esp), %esp /* avoid changing flags */
392         lret    $2
393         .size int15_e820, . - int15_e820
394         
395 /****************************************************************************
396  * INT 15,e801 handler
397  ****************************************************************************
398  */
399         .section ".text16"
400 int15_e801:
401         /* Call previous handler */
402         pushfw
403         lcall   *%cs:int15_vector
404         pushfw
405         /* Edit result */
406         pushw   %ds
407         pushw   %cs:rm_ds
408         popw    %ds
409         call    patch_1m_16m
410         xchgw   %ax, %cx
411         xchgw   %bx, %dx
412         call    patch_1m_16m
413         xchgw   %ax, %cx
414         xchgw   %bx, %dx
415         popw    %ds
416         /* Restore flags returned by previous handler and return */
417         popfw
418         lret    $2
419         .size int15_e801, . - int15_e801
420         
421 /****************************************************************************
422  * INT 15,88 handler
423  ****************************************************************************
424  */
425         .section ".text16"
426 int15_88:
427         /* Call previous handler */
428         pushfw
429         lcall   *%cs:int15_vector
430         pushfw
431         /* Edit result */
432         pushw   %ds
433         pushw   %cs:rm_ds
434         popw    %ds
435         call    patch_1m
436         popw    %ds
437         /* Restore flags returned by previous handler and return */
438         popfw
439         lret    $2
440         .size int15_88, . - int15_88
441                 
442 /****************************************************************************
443  * INT 15 handler
444  ****************************************************************************
445  */
446         .section ".text16"
447         .globl int15
448 int15:
449         /* See if we want to intercept this call */
450         pushfw
451         cmpw    $0xe820, %ax
452         jne     1f
453         cmpl    $SMAP, %edx
454         jne     1f
455         popfw
456         jmp     int15_e820
457 1:      cmpw    $0xe801, %ax
458         jne     2f
459         popfw
460         jmp     int15_e801
461 2:      cmpb    $0x88, %ah
462         jne     3f
463         popfw
464         jmp     int15_88
465 3:      popfw
466         ljmp    *%cs:int15_vector
467         .size int15, . - int15
468         
469         .section ".text16.data"
470         .globl int15_vector
471 int15_vector:
472         .long 0
473         .size int15_vector, . - int15_vector