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