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