mirror of
https://github.com/ipxe/ipxe
synced 2025-12-22 21:11:03 +03:00
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>
585 lines
15 KiB
ArmAsm
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
|