david/ipxe
Archived
1
0
This repository has been archived on 2020-12-06. You can view files and clone it, but cannot push or open issues or pull requests.
ipxe/src/arch/i386/firmware/pcbios/e820mangler.S
Michael Brown 78dd963c1f Check to see if we've reached the end of the map before attempting to
skip past an empty region, otherwise we end up generating an infinitely
long e820 map.  (Yes, there *are* real systems that provide e820 maps
with a zero-length region at the end...)
2006-08-31 13:25:45 +00:00

467 lines
11 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.
*/
.text
.arch i386
.section ".text16", "ax", @progbits
.section ".data16", "aw", @progbits
.section ".text16.data", "aw", @progbits
.code16
#define SMAP 0x534d4150
/****************************************************************************
* Check for overlap
*
* Parameters:
* %edx:%eax Region start
* %ecx:%ebx Region end
* %si Pointer to hidden region descriptor
* Returns:
* CF set Region overlaps
* CF clear No overlap
****************************************************************************
*/
.section ".text16"
check_overlap:
/* If start >= hidden_end, there is no overlap. */
testl %edx, %edx
jnz no_overlap
cmpl 4(%si), %eax
jae no_overlap
/* If end <= hidden_start, there is no overlap; equivalently,
* if end > hidden_start, there is overlap.
*/
testl %ecx, %ecx
jnz overlap
cmpl 0(%si), %ebx
ja overlap
no_overlap:
clc
ret
overlap:
stc
ret
.size check_overlap, . - check_overlap
/****************************************************************************
* Check for overflow/underflow
*
* Parameters:
* %edx:%eax Region start
* %ecx:%ebx Region end
* Returns:
* CF set start < end
* CF clear start >= end
****************************************************************************
*/
.section ".text16"
check_overflow:
pushl %ecx
pushl %ebx
subl %eax, %ebx
sbbl %edx, %ecx
popl %ebx
popl %ecx
ret
.size check_overflow, . - check_overflow
/****************************************************************************
* Truncate towards start of region
*
* Parameters:
* %edx:%eax Region start
* %ecx:%ebx Region end
* %si Pointer to hidden region descriptor
* Returns:
* %edx:%eax Modified region start
* %ecx:%ebx Modified region end
* CF set Region was truncated
* CF clear Region was not truncated
****************************************************************************
*/
.section ".text16"
truncate_to_start:
/* If overlaps, set region end = hidden region start */
call check_overlap
jnc 99f
movl 0(%si), %ebx
xorl %ecx, %ecx
/* If region end < region start, set region end = region start */
call check_overflow
jnc 1f
movl %eax, %ebx
movl %edx, %ecx
1: stc
99: ret
.size truncate_to_start, . - truncate_to_start
/****************************************************************************
* Truncate towards end of region
*
* Parameters:
* %edx:%eax Region start
* %ecx:%ebx Region end
* %si Pointer to hidden region descriptor
* Returns:
* %edx:%eax Modified region start
* %ecx:%ebx Modified region end
* CF set Region was truncated
* CF clear Region was not truncated
****************************************************************************
*/
.section ".text16"
truncate_to_end:
/* If overlaps, set region start = hidden region end */
call check_overlap
jnc 99f
movl 4(%si), %eax
xorl %edx, %edx
/* If region start > region end, set region start = region end */
call check_overflow
jnc 1f
movl %ebx, %eax
movl %ecx, %edx
1: stc
99: ret
.size truncate_to_end, . - truncate_to_end
/****************************************************************************
* Truncate region
*
* Parameters:
* %edx:%eax Region start
* %ecx:%ebx Region length (*not* region end)
* %bp truncate_to_start or truncate_to_end
* Returns:
* %edx:%eax Modified region start
* %ecx:%ebx Modified region length
* CF set Region was truncated
* CF clear Region was not truncated
****************************************************************************
*/
.section ".text16"
truncate:
pushw %si
pushfw
/* Convert (start,len) to (start,end) */
addl %eax, %ebx
adcl %edx, %ecx
/* Hide all hidden regions, truncating as directed */
movw $hidden_regions, %si
1: call *%bp
jnc 2f
popfw /* If CF was set, set stored CF in flags word on stack */
stc
pushfw
2: addw $8, %si
cmpl $0, 0(%si)
jne 1b
/* Convert modified (start,end) back to (start,len) */
subl %eax, %ebx
sbbl %edx, %ecx
popfw
popw %si
ret
.size truncate, . - truncate
/****************************************************************************
* Patch "memory above 1MB" figure
*
* Parameters:
* %ax Memory above 1MB, in 1kB blocks
* Returns:
* %ax Modified memory above 1M in 1kB blocks
* CF set Region was truncated
* CF clear Region was not truncated
****************************************************************************
*/
.section ".text16"
patch_1m:
pushal
/* Convert to (start,len) format and call truncate */
movw $truncate_to_start, %bp
xorl %ecx, %ecx
movzwl %ax, %ebx
shll $10, %ebx
xorl %edx, %edx
movl $0x100000, %eax
call truncate
/* 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
* CF set Region was truncated
* CF clear Region was not truncated
****************************************************************************
*/
.section ".text16"
patch_16m:
pushal
/* Convert to (start,len) format and call truncate */
movw $truncate_to_start, %bp
xorl %ecx, %ecx
shll $16, %ebx
xorl %edx, %edx
movl $0x1000000, %eax
call truncate
/* 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
* CF set Region was truncated
* CF clear Region was not truncated
****************************************************************************
*/
.section ".text16"
patch_1m_16m:
call patch_1m
jc 1f
call patch_16m
ret
1: /* 1m region was truncated; kill the 16m region */
xorw %bx, %bx
ret
.size patch_1m_16m, . - patch_1m_16m
/****************************************************************************
* Patch E820 memory map entry
*
* Parameters:
* %es:di Pointer to E820 memory map descriptor
* %bp truncate_to_start or truncate_to_end
* Returns:
* %es:di Pointer to now-modified E820 memory map descriptor
* CF set Region was truncated
* CF clear Region was not truncated
****************************************************************************
*/
.section ".text16"
patch_e820:
pushal
movl %es:0(%di), %eax
movl %es:4(%di), %edx
movl %es:8(%di), %ebx
movl %es:12(%di), %ecx
call truncate
movl %eax, %es:0(%di)
movl %edx, %es:4(%di)
movl %ebx, %es:8(%di)
movl %ecx, %es:12(%di)
popal
ret
.size patch_e820, . - patch_e820
/****************************************************************************
* Split E820 memory map entry if necessary
*
* Parameters:
* As for INT 15,e820
* Returns:
* As for INT 15,e820
*
* Calls the underlying INT 15,e820 and returns a modified memory map.
* Regions will be split around any hidden regions.
****************************************************************************
*/
.section ".text16"
split_e820:
pushw %si
pushw %bp
/* Caller's %bx => %si, real %ebx to %ebx, call previous handler */
pushfw
movw %bx, %si
testl %ebx, %ebx
jnz 1f
movl %ebx, %cs:real_ebx
1: movl %cs:real_ebx, %ebx
lcall *%cs:int15_vector
pushfw
/* Edit result */
pushw %ds
pushw %cs:rm_ds
popw %ds
movw $truncate_to_start, %bp
incw %si
jns 2f
movw $truncate_to_end, %bp
2: call patch_e820
jnc 3f
xorw $0x8000, %si
3: testw %si, %si
js 4f
movl %ebx, %cs:real_ebx
testl %ebx, %ebx
jz 5f
4: movw %si, %bx
5: popw %ds
/* Restore flags returned by previous handler and return */
popfw
popw %bp
popw %si
ret
.size split_e820, . - split_e820
.section ".text16.data"
real_ebx:
.long 0
.size real_ebx, . - real_ebx
/****************************************************************************
* INT 15,e820 handler
****************************************************************************
*/
.section ".text16"
int15_e820:
pushl %eax
pushl %ecx
pushl %edx
call split_e820
pushfw
/* Skip empty region checking if we've reached the end of the
* map or hit an error, to avoid a potential endless loop.
*/
jc 1f
testl %ebx, %ebx
jz 1f
/* Check for an empty region */
pushl %eax
movl %es:8(%di), %eax
orl %es:12(%di), %eax
popl %eax
jnz 1f
/* Strip empty regions out of the returned map */
popfw
popl %edx
popl %ecx
popl %eax
jmp int15_e820
/* Restore flags from original INT 15,e820 call and return */
1: popfw
addr32 leal 12(%esp), %esp /* avoid changing flags */
lret $2
.size int15_e820, . - int15_e820
/****************************************************************************
* INT 15,e801 handler
****************************************************************************
*/
.section ".text16"
int15_e801:
/* Call previous handler */
pushfw
lcall *%cs:int15_vector
pushfw
/* 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
/* Restore flags returned by previous handler and return */
popfw
lret $2
.size int15_e801, . - int15_e801
/****************************************************************************
* INT 15,88 handler
****************************************************************************
*/
.section ".text16"
int15_88:
/* Call previous handler */
pushfw
lcall *%cs:int15_vector
pushfw
/* Edit result */
pushw %ds
pushw %cs:rm_ds
popw %ds
call patch_1m
popw %ds
/* Restore flags returned by previous handler and return */
popfw
lret $2
.size int15_88, . - int15_88
/****************************************************************************
* INT 15 handler
****************************************************************************
*/
.section ".text16"
.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"
.globl int15_vector
int15_vector:
.long 0
.size int15_vector, . - int15_vector