Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / arch / i386 / firmware / pcbios / e820mangler.S
diff --git a/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/e820mangler.S b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/e820mangler.S
new file mode 100644 (file)
index 0000000..cea17ef
--- /dev/null
@@ -0,0 +1,585 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER )
+
+       .text
+       .arch i386
+       .code16
+
+#define SMAP 0x534d4150
+
+/* Most documentation refers to the E820 buffer as being 20 bytes, and
+ * the API makes it perfectly legitimate to pass only a 20-byte buffer
+ * and expect to get valid data.  However, some morons at ACPI decided
+ * to extend the data structure by adding an extra "extended
+ * attributes" field and by including critical information within this
+ * field, such as whether or not the region is enabled.  A caller who
+ * passes in only a 20-byte buffer therefore risks getting very, very
+ * misleading information.
+ *
+ * I have personally witnessed an HP BIOS that returns a value of
+ * 0x0009 in the extended attributes field.  If we don't pass this
+ * value through to the caller, 32-bit WinPE will die, usually with a
+ * PAGE_FAULT_IN_NONPAGED_AREA blue screen of death.
+ *
+ * Allow a ridiculously large maximum value (64 bytes) for the E820
+ * buffer as a guard against insufficiently creative idiots in the
+ * future.
+ */
+#define E820MAXSIZE    64
+
+/****************************************************************************
+ *
+ * Allowed memory windows
+ *
+ * There are two ways to view this list.  The first is as a list of
+ * (non-overlapping) allowed memory regions, sorted by increasing
+ * address.  The second is as a list of (non-overlapping) hidden
+ * memory regions, again sorted by increasing address.  The second
+ * view is offset by half an entry from the first: think about this
+ * for a moment and it should make sense.
+ *
+ * xxx_memory_window is used to indicate an "allowed region"
+ * structure, hidden_xxx_memory is used to indicate a "hidden region"
+ * structure.  Each structure is 16 bytes in length.
+ *
+ ****************************************************************************
+ */
+       .section ".data16", "aw", @progbits
+       .align 16
+       .globl hidemem_base
+       .globl hidemem_umalloc
+       .globl hidemem_textdata
+memory_windows:
+base_memory_window:    .long 0x00000000, 0x00000000 /* Start of memory */
+
+hidemem_base:          .long 0x000a0000, 0x00000000 /* Changes at runtime */
+ext_memory_window:     .long 0x000a0000, 0x00000000 /* 640kB mark */
+
+hidemem_umalloc:       .long 0xffffffff, 0xffffffff /* Changes at runtime */
+                       .long 0xffffffff, 0xffffffff /* Changes at runtime */
+
+hidemem_textdata:      .long 0xffffffff, 0xffffffff /* Changes at runtime */
+                       .long 0xffffffff, 0xffffffff /* Changes at runtime */
+
+                       .long 0xffffffff, 0xffffffff /* End of memory */
+memory_windows_end:
+
+/****************************************************************************
+ * Truncate region to memory window
+ *
+ * Parameters:
+ *  %edx:%eax  Start of region
+ *  %ecx:%ebx  Length of region
+ *  %si                Memory window
+ * Returns:
+ *  %edx:%eax  Start of windowed region
+ *  %ecx:%ebx  Length of windowed region
+ ****************************************************************************
+ */
+       .section ".text16", "ax", @progbits
+window_region:
+       /* Convert (start,len) to (start, end) */
+       addl    %eax, %ebx
+       adcl    %edx, %ecx
+       /* Truncate to window start */
+       cmpl    4(%si), %edx
+       jne     1f
+       cmpl    0(%si), %eax
+1:     jae     2f
+       movl    4(%si), %edx
+       movl    0(%si), %eax
+2:     /* Truncate to window end */
+       cmpl    12(%si), %ecx
+       jne     1f
+       cmpl    8(%si), %ebx
+1:     jbe     2f
+       movl    12(%si), %ecx
+       movl    8(%si), %ebx
+2:     /* Convert (start, end) back to (start, len) */
+       subl    %eax, %ebx
+       sbbl    %edx, %ecx
+       /* If length is <0, set length to 0 */
+       jae     1f
+       xorl    %ebx, %ebx
+       xorl    %ecx, %ecx
+       ret
+       .size   window_region, . - window_region
+
+/****************************************************************************
+ * Patch "memory above 1MB" figure
+ *
+ * Parameters:
+ *  %ax                Memory above 1MB, in 1kB blocks
+ * Returns:
+ *  %ax                Modified memory above 1M in 1kB blocks
+ ****************************************************************************
+ */
+       .section ".text16", "ax", @progbits
+patch_1m:
+       pushal
+       /* Convert to (start,len) format and call truncate */
+       xorl    %ecx, %ecx
+       movzwl  %ax, %ebx
+       shll    $10, %ebx
+       xorl    %edx, %edx
+       movl    $0x100000, %eax
+       movw    $ext_memory_window, %si
+       call    window_region
+       /* Convert back to "memory above 1MB" format and return via %ax */
+       pushfw
+       shrl    $10, %ebx
+       popfw
+       movw    %sp, %bp
+       movw    %bx, 28(%bp)
+       popal
+       ret
+       .size patch_1m, . - patch_1m
+
+/****************************************************************************
+ * Patch "memory above 16MB" figure
+ *
+ * Parameters:
+ *  %bx                Memory above 16MB, in 64kB blocks
+ * Returns:
+ *  %bx                Modified memory above 16M in 64kB blocks
+ ****************************************************************************
+ */
+       .section ".text16", "ax", @progbits
+patch_16m:
+       pushal
+       /* Convert to (start,len) format and call truncate */
+       xorl    %ecx, %ecx
+       shll    $16, %ebx
+       xorl    %edx, %edx
+       movl    $0x1000000, %eax
+       movw    $ext_memory_window, %si
+       call    window_region
+       /* Convert back to "memory above 16MB" format and return via %bx */
+       pushfw
+       shrl    $16, %ebx
+       popfw
+       movw    %sp, %bp
+       movw    %bx, 16(%bp)
+       popal
+       ret
+       .size patch_16m, . - patch_16m
+
+/****************************************************************************
+ * Patch "memory between 1MB and 16MB" and "memory above 16MB" figures
+ *
+ * Parameters:
+ *  %ax                Memory between 1MB and 16MB, in 1kB blocks
+ *  %bx                Memory above 16MB, in 64kB blocks
+ * Returns:
+ *  %ax                Modified memory between 1MB and 16MB, in 1kB blocks
+ *  %bx                Modified memory above 16MB, in 64kB blocks
+ ****************************************************************************
+ */
+       .section ".text16", "ax", @progbits
+patch_1m_16m:
+       call    patch_1m
+       call    patch_16m
+       /* If 1M region is no longer full-length, kill off the 16M region */
+       cmpw    $( 15 * 1024 ), %ax
+       je      1f
+       xorw    %bx, %bx
+1:     ret
+       .size patch_1m_16m, . - patch_1m_16m
+
+/****************************************************************************
+ * Get underlying e820 memory region to underlying_e820 buffer
+ *
+ * Parameters:
+ *   As for INT 15,e820
+ * Returns:
+ *   As for INT 15,e820
+ *
+ * Wraps the underlying INT 15,e820 call so that the continuation
+ * value (%ebx) is a 16-bit simple sequence counter (with the high 16
+ * bits ignored), and termination is always via CF=1 rather than
+ * %ebx=0.
+ *
+ ****************************************************************************
+ */
+       .section ".text16", "ax", @progbits
+get_underlying_e820:
+
+       /* If the requested region is in the cache, return it */
+       cmpw    %bx, underlying_e820_index
+       jne     2f
+       pushw   %di
+       pushw   %si
+       movw    $underlying_e820_cache, %si
+       cmpl    underlying_e820_cache_size, %ecx
+       jbe     1f
+       movl    underlying_e820_cache_size, %ecx
+1:     pushl   %ecx
+       rep movsb
+       popl    %ecx
+       popw    %si
+       popw    %di
+       incw    %bx
+       movl    %edx, %eax
+       clc
+       ret
+2:     
+       /* If the requested region is earlier than the cached region,
+        * invalidate the cache.
+        */
+       cmpw    %bx, underlying_e820_index
+       jbe     1f
+       movw    $0xffff, underlying_e820_index
+1:
+       /* If the cache is invalid, reset the underlying %ebx */
+       cmpw    $0xffff, underlying_e820_index
+       jne     1f
+       andl    $0, underlying_e820_ebx
+1:     
+       /* If the cache is valid but the continuation value is zero,
+        * this means that the previous underlying call returned with
+        * %ebx=0.  Return with CF=1 in this case.
+        */
+       cmpw    $0xffff, underlying_e820_index
+       je      1f
+       cmpl    $0, underlying_e820_ebx
+       jne     1f
+       stc
+       ret
+1:     
+       /* Get the next region into the cache */
+       pushl   %eax
+       pushl   %ebx
+       pushl   %ecx
+       pushl   %edx
+       pushl   %esi    /* Some implementations corrupt %esi, so we     */
+       pushl   %edi    /* preserve %esi, %edi and %ebp to be paranoid  */
+       pushl   %ebp
+       pushw   %es
+       pushw   %ds
+       popw    %es
+       movw    $underlying_e820_cache, %di
+       cmpl    $E820MAXSIZE, %ecx
+       jbe     1f
+       movl    $E820MAXSIZE, %ecx
+1:     movl    underlying_e820_ebx, %ebx
+       stc
+       pushfw
+       lcall   *%cs:int15_vector
+       popw    %es
+       popl    %ebp
+       popl    %edi
+       popl    %esi
+       /* Check for error return from underlying e820 call */
+       jc      2f /* CF set: error */
+       cmpl    $SMAP, %eax
+       je      3f /* 'SMAP' missing: error */
+2:     /* An error occurred: return values returned by underlying e820 call */
+       stc     /* Force CF set if SMAP was missing */
+       addr32 leal 16(%esp), %esp /* avoid changing other flags */
+       ret
+3:     /* No error occurred */
+       movl    %ebx, underlying_e820_ebx
+       movl    %ecx, underlying_e820_cache_size
+       popl    %edx
+       popl    %ecx
+       popl    %ebx
+       popl    %eax
+       /* Mark cache as containing this result */
+       incw    underlying_e820_index
+
+       /* Loop until found */
+       jmp     get_underlying_e820
+       .size   get_underlying_e820, . - get_underlying_e820
+
+       .section ".data16", "aw", @progbits
+underlying_e820_index:
+       .word   0xffff /* Initialise to an invalid value */
+       .size underlying_e820_index, . - underlying_e820_index
+
+       .section ".bss16", "aw", @nobits
+underlying_e820_ebx:
+       .long   0
+       .size underlying_e820_ebx, . - underlying_e820_ebx
+
+       .section ".bss16", "aw", @nobits
+underlying_e820_cache:
+       .space  E820MAXSIZE
+       .size underlying_e820_cache, . - underlying_e820_cache
+
+       .section ".bss16", "aw", @nobits
+underlying_e820_cache_size:
+       .long   0
+       .size   underlying_e820_cache_size, . - underlying_e820_cache_size
+
+/****************************************************************************
+ * Get windowed e820 region, without empty region stripping
+ *
+ * Parameters:
+ *   As for INT 15,e820
+ * Returns:
+ *   As for INT 15,e820
+ *
+ * Wraps the underlying INT 15,e820 call so that each underlying
+ * region is returned N times, windowed to fit within N visible-memory
+ * windows.  Termination is always via CF=1.
+ *
+ ****************************************************************************
+ */
+       .section ".text16", "ax", @progbits
+get_windowed_e820:
+
+       /* Preserve registers */
+       pushl   %esi
+       pushw   %bp
+
+       /* Split %ebx into %si:%bx, store original %bx in %bp */
+       pushl   %ebx
+       popw    %bp
+       popw    %si
+
+       /* %si == 0 => start of memory_windows list */
+       testw   %si, %si
+       jne     1f
+       movw    $memory_windows, %si
+1:     
+       /* Get (cached) underlying e820 region to buffer */
+       call    get_underlying_e820
+       jc      99f /* Abort on error */
+
+       /* Preserve registers */
+       pushal
+       /* start => %edx:%eax, len => %ecx:%ebx */
+       movl    %es:0(%di), %eax
+       movl    %es:4(%di), %edx
+       movl    %es:8(%di), %ebx
+       movl    %es:12(%di), %ecx
+       /* Truncate region to current window */
+       call    window_region
+1:     /* Store modified values in e820 map entry */
+       movl    %eax, %es:0(%di)
+       movl    %edx, %es:4(%di)
+       movl    %ebx, %es:8(%di)
+       movl    %ecx, %es:12(%di)
+       /* Restore registers */
+       popal
+
+       /* Derive continuation value for next call */
+       addw    $16, %si
+       cmpw    $memory_windows_end, %si
+       jne     1f
+       /* End of memory windows: reset %si and allow %bx to continue */
+       xorw    %si, %si
+       jmp     2f
+1:     /* More memory windows to go: restore original %bx */
+       movw    %bp, %bx
+2:     /* Construct %ebx from %si:%bx */
+       pushw   %si
+       pushw   %bx
+       popl    %ebx
+
+98:    /* Clear CF */
+       clc
+99:    /* Restore registers and return */
+       popw    %bp
+       popl    %esi
+       ret
+       .size get_windowed_e820, . - get_windowed_e820
+
+/****************************************************************************
+ * Get windowed e820 region, with empty region stripping
+ *
+ * Parameters:
+ *   As for INT 15,e820
+ * Returns:
+ *   As for INT 15,e820
+ *
+ * Wraps the underlying INT 15,e820 call so that each underlying
+ * region is returned up to N times, windowed to fit within N
+ * visible-memory windows.  Empty windows are never returned.
+ * Termination is always via CF=1.
+ *
+ ****************************************************************************
+ */
+       .section ".text16", "ax", @progbits
+get_nonempty_e820:
+
+       /* Record entry parameters */
+       pushl   %eax
+       pushl   %ecx
+       pushl   %edx
+
+       /* Get next windowed region */
+       call    get_windowed_e820
+       jc      99f /* abort on error */
+
+       /* If region is non-empty, finish here */
+       cmpl    $0, %es:8(%di)
+       jne     98f
+       cmpl    $0, %es:12(%di)
+       jne     98f
+
+       /* Region was empty: restore entry parameters and go to next region */
+       popl    %edx
+       popl    %ecx
+       popl    %eax
+       jmp     get_nonempty_e820
+
+98:    /* Clear CF */
+       clc
+99:    /* Return values from underlying call */
+       addr32 leal 12(%esp), %esp /* avoid changing flags */
+       ret
+       .size get_nonempty_e820, . - get_nonempty_e820
+
+/****************************************************************************
+ * Get mangled e820 region, with empty region stripping
+ *
+ * Parameters:
+ *   As for INT 15,e820
+ * Returns:
+ *   As for INT 15,e820
+ *
+ * Wraps the underlying INT 15,e820 call so that underlying regions
+ * are windowed to the allowed memory regions.  Empty regions are
+ * stripped from the map.  Termination is always via %ebx=0.
+ *
+ ****************************************************************************
+ */
+       .section ".text16", "ax", @progbits
+get_mangled_e820:
+
+       /* Get a nonempty region */
+       call    get_nonempty_e820
+       jc      99f /* Abort on error */
+
+       /* Peek ahead to see if there are any further nonempty regions */
+       pushal
+       pushw   %es
+       movw    %sp, %bp
+       subw    %cx, %sp
+       movl    $0xe820, %eax
+       movl    $SMAP, %edx
+       pushw   %ss
+       popw    %es
+       movw    %sp, %di
+       call    get_nonempty_e820
+       movw    %bp, %sp
+       popw    %es
+       popal
+       jnc     99f /* There are further nonempty regions */
+
+       /* No futher nonempty regions: zero %ebx and clear CF */
+       xorl    %ebx, %ebx
+       
+99:    /* Return */
+       ret
+       .size get_mangled_e820, . - get_mangled_e820
+
+/****************************************************************************
+ * INT 15,e820 handler
+ ****************************************************************************
+ */
+       .section ".text16", "ax", @progbits
+int15_e820:
+       pushw   %ds
+       pushw   %cs:rm_ds
+       popw    %ds
+       call    get_mangled_e820
+       popw    %ds
+       call    patch_cf
+       iret
+       .size int15_e820, . - int15_e820
+       
+/****************************************************************************
+ * INT 15,e801 handler
+ ****************************************************************************
+ */
+       .section ".text16", "ax", @progbits
+int15_e801:
+       /* Call previous handler */
+       pushfw
+       lcall   *%cs:int15_vector
+       call    patch_cf
+       /* Edit result */
+       pushw   %ds
+       pushw   %cs:rm_ds
+       popw    %ds
+       call    patch_1m_16m
+       xchgw   %ax, %cx
+       xchgw   %bx, %dx
+       call    patch_1m_16m
+       xchgw   %ax, %cx
+       xchgw   %bx, %dx
+       popw    %ds
+       iret
+       .size int15_e801, . - int15_e801
+       
+/****************************************************************************
+ * INT 15,88 handler
+ ****************************************************************************
+ */
+       .section ".text16", "ax", @progbits
+int15_88:
+       /* Call previous handler */
+       pushfw
+       lcall   *%cs:int15_vector
+       call    patch_cf
+       /* Edit result */
+       pushw   %ds
+       pushw   %cs:rm_ds
+       popw    %ds
+       call    patch_1m
+       popw    %ds
+       iret
+       .size int15_88, . - int15_88
+               
+/****************************************************************************
+ * INT 15 handler
+ ****************************************************************************
+ */
+       .section ".text16", "ax", @progbits
+       .globl int15
+int15:
+       /* See if we want to intercept this call */
+       pushfw
+       cmpw    $0xe820, %ax
+       jne     1f
+       cmpl    $SMAP, %edx
+       jne     1f
+       popfw
+       jmp     int15_e820
+1:     cmpw    $0xe801, %ax
+       jne     2f
+       popfw
+       jmp     int15_e801
+2:     cmpb    $0x88, %ah
+       jne     3f
+       popfw
+       jmp     int15_88
+3:     popfw
+       ljmp    *%cs:int15_vector
+       .size int15, . - int15
+       
+       .section ".text16.data", "aw", @progbits
+       .globl int15_vector
+int15_vector:
+       .long 0
+       .size int15_vector, . - int15_vector