Files
ipxe/src/arch/i386/firmware/pcbios/e820mangler.S
Stefan Hajnoczi 0d108681ac [comboot] Propagate carry flag from COMBOOT API
COMBOOT API calls set the carry flag on failure.  This was not being
propagated because the COMBOOT interrupt handler used iret to return
with EFLAGS restored from the stack.  This patch propagates CF before
returning from the interrupt.

Reported-by: Geoff Lywood <glywood@vmware.com>
Signed-off-by: Stefan Hajnoczi <stefanha@gmail.com>
Signed-off-by: Marty Connor <mdc@etherboot.org>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2010-05-25 06:24:58 +01:00

585 lines
15 KiB
ArmAsm

/*
* 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., 675 Mass Ave, Cambridge, MA 02139, 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