[pcbios] Don't use "lret $2" to return from an interrupt
[people/lynusvaz/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         .code16
22
23 #define SMAP 0x534d4150
24
25 /* Most documentation refers to the E820 buffer as being 20 bytes, and
26  * the API makes it perfectly legitimate to pass only a 20-byte buffer
27  * and expect to get valid data.  However, some morons at ACPI decided
28  * to extend the data structure by adding an extra "extended
29  * attributes" field and by including critical information within this
30  * field, such as whether or not the region is enabled.  A caller who
31  * passes in only a 20-byte buffer therefore risks getting very, very
32  * misleading information.
33  *
34  * I have personally witnessed an HP BIOS that returns a value of
35  * 0x0009 in the extended attributes field.  If we don't pass this
36  * value through to the caller, 32-bit WinPE will die, usually with a
37  * PAGE_FAULT_IN_NONPAGED_AREA blue screen of death.
38  *
39  * Allow a ridiculously large maximum value (64 bytes) for the E820
40  * buffer as a guard against insufficiently creative idiots in the
41  * future.
42  */
43 #define E820MAXSIZE     64
44
45 /****************************************************************************
46  *
47  * Allowed memory windows
48  *
49  * There are two ways to view this list.  The first is as a list of
50  * (non-overlapping) allowed memory regions, sorted by increasing
51  * address.  The second is as a list of (non-overlapping) hidden
52  * memory regions, again sorted by increasing address.  The second
53  * view is offset by half an entry from the first: think about this
54  * for a moment and it should make sense.
55  *
56  * xxx_memory_window is used to indicate an "allowed region"
57  * structure, hidden_xxx_memory is used to indicate a "hidden region"
58  * structure.  Each structure is 16 bytes in length.
59  *
60  ****************************************************************************
61  */
62         .section ".data16", "aw", @progbits
63         .align 16
64         .globl hidemem_base
65         .globl hidemem_umalloc
66         .globl hidemem_textdata
67 memory_windows:
68 base_memory_window:     .long 0x00000000, 0x00000000 /* Start of memory */
69
70 hidemem_base:           .long 0x000a0000, 0x00000000 /* Changes at runtime */
71 ext_memory_window:      .long 0x000a0000, 0x00000000 /* 640kB mark */
72
73 hidemem_umalloc:        .long 0xffffffff, 0xffffffff /* Changes at runtime */
74                         .long 0xffffffff, 0xffffffff /* Changes at runtime */
75
76 hidemem_textdata:       .long 0xffffffff, 0xffffffff /* Changes at runtime */
77                         .long 0xffffffff, 0xffffffff /* Changes at runtime */
78
79                         .long 0xffffffff, 0xffffffff /* End of memory */
80 memory_windows_end:
81
82 /****************************************************************************
83  * Truncate region to memory window
84  *
85  * Parameters:
86  *  %edx:%eax   Start of region
87  *  %ecx:%ebx   Length of region
88  *  %si         Memory window
89  * Returns:
90  *  %edx:%eax   Start of windowed region
91  *  %ecx:%ebx   Length of windowed region
92  ****************************************************************************
93  */
94         .section ".text16", "ax", @progbits
95 window_region:
96         /* Convert (start,len) to (start, end) */
97         addl    %eax, %ebx
98         adcl    %edx, %ecx
99         /* Truncate to window start */
100         cmpl    4(%si), %edx
101         jne     1f
102         cmpl    0(%si), %eax
103 1:      jae     2f
104         movl    4(%si), %edx
105         movl    0(%si), %eax
106 2:      /* Truncate to window end */
107         cmpl    12(%si), %ecx
108         jne     1f
109         cmpl    8(%si), %ebx
110 1:      jbe     2f
111         movl    12(%si), %ecx
112         movl    8(%si), %ebx
113 2:      /* Convert (start, end) back to (start, len) */
114         subl    %eax, %ebx
115         sbbl    %edx, %ecx
116         /* If length is <0, set length to 0 */
117         jae     1f
118         xorl    %ebx, %ebx
119         xorl    %ecx, %ecx
120         ret
121         .size   window_region, . - window_region
122
123 /****************************************************************************
124  * Patch "memory above 1MB" figure
125  *
126  * Parameters:
127  *  %ax         Memory above 1MB, in 1kB blocks
128  * Returns:
129  *  %ax         Modified memory above 1M in 1kB blocks
130  ****************************************************************************
131  */
132         .section ".text16", "ax", @progbits
133 patch_1m:
134         pushal
135         /* Convert to (start,len) format and call truncate */
136         xorl    %ecx, %ecx
137         movzwl  %ax, %ebx
138         shll    $10, %ebx
139         xorl    %edx, %edx
140         movl    $0x100000, %eax
141         movw    $ext_memory_window, %si
142         call    window_region
143         /* Convert back to "memory above 1MB" format and return via %ax */
144         pushfw
145         shrl    $10, %ebx
146         popfw
147         movw    %sp, %bp
148         movw    %bx, 28(%bp)
149         popal
150         ret
151         .size patch_1m, . - patch_1m
152
153 /****************************************************************************
154  * Patch "memory above 16MB" figure
155  *
156  * Parameters:
157  *  %bx         Memory above 16MB, in 64kB blocks
158  * Returns:
159  *  %bx         Modified memory above 16M in 64kB blocks
160  ****************************************************************************
161  */
162         .section ".text16", "ax", @progbits
163 patch_16m:
164         pushal
165         /* Convert to (start,len) format and call truncate */
166         xorl    %ecx, %ecx
167         shll    $16, %ebx
168         xorl    %edx, %edx
169         movl    $0x1000000, %eax
170         movw    $ext_memory_window, %si
171         call    window_region
172         /* Convert back to "memory above 16MB" format and return via %bx */
173         pushfw
174         shrl    $16, %ebx
175         popfw
176         movw    %sp, %bp
177         movw    %bx, 16(%bp)
178         popal
179         ret
180         .size patch_16m, . - patch_16m
181
182 /****************************************************************************
183  * Patch "memory between 1MB and 16MB" and "memory above 16MB" figures
184  *
185  * Parameters:
186  *  %ax         Memory between 1MB and 16MB, in 1kB blocks
187  *  %bx         Memory above 16MB, in 64kB blocks
188  * Returns:
189  *  %ax         Modified memory between 1MB and 16MB, in 1kB blocks
190  *  %bx         Modified memory above 16MB, in 64kB blocks
191  ****************************************************************************
192  */
193         .section ".text16", "ax", @progbits
194 patch_1m_16m:
195         call    patch_1m
196         call    patch_16m
197         /* If 1M region is no longer full-length, kill off the 16M region */
198         cmpw    $( 15 * 1024 ), %ax
199         je      1f
200         xorw    %bx, %bx
201 1:      ret
202         .size patch_1m_16m, . - patch_1m_16m
203
204 /****************************************************************************
205  * Get underlying e820 memory region to underlying_e820 buffer
206  *
207  * Parameters:
208  *   As for INT 15,e820
209  * Returns:
210  *   As for INT 15,e820
211  *
212  * Wraps the underlying INT 15,e820 call so that the continuation
213  * value (%ebx) is a 16-bit simple sequence counter (with the high 16
214  * bits ignored), and termination is always via CF=1 rather than
215  * %ebx=0.
216  *
217  ****************************************************************************
218  */
219         .section ".text16", "ax", @progbits
220 get_underlying_e820:
221
222         /* If the requested region is in the cache, return it */
223         cmpw    %bx, underlying_e820_index
224         jne     2f
225         pushw   %di
226         pushw   %si
227         movw    $underlying_e820_cache, %si
228         cmpl    underlying_e820_cache_size, %ecx
229         jbe     1f
230         movl    underlying_e820_cache_size, %ecx
231 1:      pushl   %ecx
232         rep movsb
233         popl    %ecx
234         popw    %si
235         popw    %di
236         incw    %bx
237         movl    %edx, %eax
238         ret
239 2:      
240         /* If the requested region is earlier than the cached region,
241          * invalidate the cache.
242          */
243         cmpw    %bx, underlying_e820_index
244         jbe     1f
245         movw    $0xffff, underlying_e820_index
246 1:
247         /* If the cache is invalid, reset the underlying %ebx */
248         cmpw    $0xffff, underlying_e820_index
249         jne     1f
250         andl    $0, underlying_e820_ebx
251 1:      
252         /* If the cache is valid but the continuation value is zero,
253          * this means that the previous underlying call returned with
254          * %ebx=0.  Return with CF=1 in this case.
255          */
256         cmpw    $0xffff, underlying_e820_index
257         je      1f
258         cmpl    $0, underlying_e820_ebx
259         jne     1f
260         stc
261         ret
262 1:      
263         /* Get the next region into the cache */
264         pushl   %eax
265         pushl   %ebx
266         pushl   %ecx
267         pushl   %edx
268         pushl   %esi    /* Some implementations corrupt %esi, so we     */
269         pushl   %edi    /* preserve %esi, %edi and %ebp to be paranoid  */
270         pushl   %ebp
271         pushw   %es
272         pushw   %ds
273         popw    %es
274         movw    $underlying_e820_cache, %di
275         cmpl    $E820MAXSIZE, %ecx
276         jbe     1f
277         movl    $E820MAXSIZE, %ecx
278 1:      movl    underlying_e820_ebx, %ebx
279         stc
280         pushfw
281         lcall   *%cs:int15_vector
282         popw    %es
283         popl    %ebp
284         popl    %edi
285         popl    %esi
286         /* Check for error return from underlying e820 call */
287         jc      2f /* CF set: error */
288         cmpl    $SMAP, %eax
289         je      3f /* 'SMAP' missing: error */
290 2:      /* An error occurred: return values returned by underlying e820 call */
291         stc     /* Force CF set if SMAP was missing */
292         addr32 leal 16(%esp), %esp /* avoid changing other flags */
293         ret
294 3:      /* No error occurred */
295         movl    %ebx, underlying_e820_ebx
296         movl    %ecx, underlying_e820_cache_size
297         popl    %edx
298         popl    %ecx
299         popl    %ebx
300         popl    %eax
301         /* Mark cache as containing this result */
302         incw    underlying_e820_index
303
304         /* Loop until found */
305         jmp     get_underlying_e820
306         .size   get_underlying_e820, . - get_underlying_e820
307
308         .section ".data16", "aw", @progbits
309 underlying_e820_index:
310         .word   0xffff /* Initialise to an invalid value */
311         .size underlying_e820_index, . - underlying_e820_index
312
313         .section ".bss16", "aw", @nobits
314 underlying_e820_ebx:
315         .long   0
316         .size underlying_e820_ebx, . - underlying_e820_ebx
317
318         .section ".bss16", "aw", @nobits
319 underlying_e820_cache:
320         .space  E820MAXSIZE
321         .size underlying_e820_cache, . - underlying_e820_cache
322
323         .section ".bss16", "aw", @nobits
324 underlying_e820_cache_size:
325         .long   0
326         .size   underlying_e820_cache_size, . - underlying_e820_cache_size
327
328 /****************************************************************************
329  * Get windowed e820 region, without empty region stripping
330  *
331  * Parameters:
332  *   As for INT 15,e820
333  * Returns:
334  *   As for INT 15,e820
335  *
336  * Wraps the underlying INT 15,e820 call so that each underlying
337  * region is returned N times, windowed to fit within N visible-memory
338  * windows.  Termination is always via CF=1.
339  *
340  ****************************************************************************
341  */
342         .section ".text16", "ax", @progbits
343 get_windowed_e820:
344
345         /* Preserve registers */
346         pushl   %esi
347         pushw   %bp
348
349         /* Split %ebx into %si:%bx, store original %bx in %bp */
350         pushl   %ebx
351         popw    %bp
352         popw    %si
353
354         /* %si == 0 => start of memory_windows list */
355         testw   %si, %si
356         jne     1f
357         movw    $memory_windows, %si
358 1:      
359         /* Get (cached) underlying e820 region to buffer */
360         call    get_underlying_e820
361         jc      99f /* Abort on error */
362
363         /* Preserve registers */
364         pushal
365         /* start => %edx:%eax, len => %ecx:%ebx */
366         movl    %es:0(%di), %eax
367         movl    %es:4(%di), %edx
368         movl    %es:8(%di), %ebx
369         movl    %es:12(%di), %ecx
370         /* Truncate region to current window */
371         call    window_region
372 1:      /* Store modified values in e820 map entry */
373         movl    %eax, %es:0(%di)
374         movl    %edx, %es:4(%di)
375         movl    %ebx, %es:8(%di)
376         movl    %ecx, %es:12(%di)
377         /* Restore registers */
378         popal
379
380         /* Derive continuation value for next call */
381         addw    $16, %si
382         cmpw    $memory_windows_end, %si
383         jne     1f
384         /* End of memory windows: reset %si and allow %bx to continue */
385         xorw    %si, %si
386         jmp     2f
387 1:      /* More memory windows to go: restore original %bx */
388         movw    %bp, %bx
389 2:      /* Construct %ebx from %si:%bx */
390         pushw   %si
391         pushw   %bx
392         popl    %ebx
393
394 98:     /* Clear CF */
395         clc
396 99:     /* Restore registers and return */
397         popw    %bp
398         popl    %esi
399         ret
400         .size get_windowed_e820, . - get_windowed_e820
401
402 /****************************************************************************
403  * Get windowed e820 region, with empty region stripping
404  *
405  * Parameters:
406  *   As for INT 15,e820
407  * Returns:
408  *   As for INT 15,e820
409  *
410  * Wraps the underlying INT 15,e820 call so that each underlying
411  * region is returned up to N times, windowed to fit within N
412  * visible-memory windows.  Empty windows are never returned.
413  * Termination is always via CF=1.
414  *
415  ****************************************************************************
416  */
417         .section ".text16", "ax", @progbits
418 get_nonempty_e820:
419
420         /* Record entry parameters */
421         pushl   %eax
422         pushl   %ecx
423         pushl   %edx
424
425         /* Get next windowed region */
426         call    get_windowed_e820
427         jc      99f /* abort on error */
428
429         /* If region is non-empty, finish here */
430         cmpl    $0, %es:8(%di)
431         jne     98f
432         cmpl    $0, %es:12(%di)
433         jne     98f
434
435         /* Region was empty: restore entry parameters and go to next region */
436         popl    %edx
437         popl    %ecx
438         popl    %eax
439         jmp     get_nonempty_e820
440
441 98:     /* Clear CF */
442         clc
443 99:     /* Return values from underlying call */
444         addr32 leal 12(%esp), %esp /* avoid changing flags */
445         ret
446         .size get_nonempty_e820, . - get_nonempty_e820
447
448 /****************************************************************************
449  * Get mangled e820 region, with empty region stripping
450  *
451  * Parameters:
452  *   As for INT 15,e820
453  * Returns:
454  *   As for INT 15,e820
455  *
456  * Wraps the underlying INT 15,e820 call so that underlying regions
457  * are windowed to the allowed memory regions.  Empty regions are
458  * stripped from the map.  Termination is always via %ebx=0.
459  *
460  ****************************************************************************
461  */
462         .section ".text16", "ax", @progbits
463 get_mangled_e820:
464
465         /* Get a nonempty region */
466         call    get_nonempty_e820
467         jc      99f /* Abort on error */
468
469         /* Peek ahead to see if there are any further nonempty regions */
470         pushal
471         pushw   %es
472         movw    %sp, %bp
473         subw    %cx, %sp
474         movl    $0xe820, %eax
475         movl    $SMAP, %edx
476         pushw   %ss
477         popw    %es
478         movw    %sp, %di
479         call    get_nonempty_e820
480         movw    %bp, %sp
481         popw    %es
482         popal
483         jnc     99f /* There are further nonempty regions */
484
485         /* No futher nonempty regions: zero %ebx and clear CF */
486         xorl    %ebx, %ebx
487         
488 99:     /* Return */
489         ret
490         .size get_mangled_e820, . - get_mangled_e820
491
492 /****************************************************************************
493  * Set/clear CF on the stack as appropriate, assumes stack is as it should
494  * be immediately before IRET
495  ****************************************************************************
496  */
497 patch_cf:
498         pushw   %bp
499         movw    %sp, %bp
500         setc    8(%bp)  /* Set/reset CF; clears PF, AF, ZF, SF */
501         popw    %bp
502         ret
503
504 /****************************************************************************
505  * INT 15,e820 handler
506  ****************************************************************************
507  */
508         .section ".text16", "ax", @progbits
509 int15_e820:
510         pushw   %ds
511         pushw   %cs:rm_ds
512         popw    %ds
513         call    get_mangled_e820
514         popw    %ds
515         call    patch_cf
516         iret
517         .size int15_e820, . - int15_e820
518         
519 /****************************************************************************
520  * INT 15,e801 handler
521  ****************************************************************************
522  */
523         .section ".text16", "ax", @progbits
524 int15_e801:
525         /* Call previous handler */
526         pushfw
527         lcall   *%cs:int15_vector
528         call    patch_cf
529         /* Edit result */
530         pushw   %ds
531         pushw   %cs:rm_ds
532         popw    %ds
533         call    patch_1m_16m
534         xchgw   %ax, %cx
535         xchgw   %bx, %dx
536         call    patch_1m_16m
537         xchgw   %ax, %cx
538         xchgw   %bx, %dx
539         popw    %ds
540         iret
541         .size int15_e801, . - int15_e801
542         
543 /****************************************************************************
544  * INT 15,88 handler
545  ****************************************************************************
546  */
547         .section ".text16", "ax", @progbits
548 int15_88:
549         /* Call previous handler */
550         pushfw
551         lcall   *%cs:int15_vector
552         call    patch_cf
553         /* Edit result */
554         pushw   %ds
555         pushw   %cs:rm_ds
556         popw    %ds
557         call    patch_1m
558         popw    %ds
559         iret
560         .size int15_88, . - int15_88
561                 
562 /****************************************************************************
563  * INT 15 handler
564  ****************************************************************************
565  */
566         .section ".text16", "ax", @progbits
567         .globl int15
568 int15:
569         /* See if we want to intercept this call */
570         pushfw
571         cmpw    $0xe820, %ax
572         jne     1f
573         cmpl    $SMAP, %edx
574         jne     1f
575         popfw
576         jmp     int15_e820
577 1:      cmpw    $0xe801, %ax
578         jne     2f
579         popfw
580         jmp     int15_e801
581 2:      cmpb    $0x88, %ah
582         jne     3f
583         popfw
584         jmp     int15_88
585 3:      popfw
586         ljmp    *%cs:int15_vector
587         .size int15, . - int15
588         
589         .section ".text16.data", "aw", @progbits
590         .globl int15_vector
591 int15_vector:
592         .long 0
593         .size int15_vector, . - int15_vector