2010-04-19 20:16:01 +01:00
|
|
|
#include <ipxe/io.h>
|
2006-05-02 15:41:21 +00:00
|
|
|
#include <registers.h>
|
2005-03-08 18:53:11 +00:00
|
|
|
|
2005-04-09 13:12:22 +00:00
|
|
|
/*
|
2006-05-02 15:41:21 +00:00
|
|
|
* Originally by Eric Biederman
|
|
|
|
|
*
|
|
|
|
|
* Heavily modified by Michael Brown
|
2005-04-09 13:12:22 +00:00
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
2015-03-02 13:29:46 +00:00
|
|
|
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
2009-05-01 15:41:06 +01:00
|
|
|
|
2005-04-09 13:12:22 +00:00
|
|
|
/*
|
|
|
|
|
* The linker passes in the symbol _max_align, which is the alignment
|
|
|
|
|
* that we must preserve, in bytes.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
extern char _max_align[];
|
|
|
|
|
#define max_align ( ( unsigned int ) _max_align )
|
|
|
|
|
|
|
|
|
|
/* Linker symbols */
|
2008-10-09 22:22:01 +01:00
|
|
|
extern char _textdata[];
|
|
|
|
|
extern char _etextdata[];
|
2005-04-27 12:10:24 +00:00
|
|
|
|
2006-05-02 15:41:21 +00:00
|
|
|
/* within 1MB of 4GB is too close.
|
|
|
|
|
* MAX_ADDR is the maximum address we can easily do DMA to.
|
|
|
|
|
*
|
|
|
|
|
* Not sure where this constraint comes from, but kept it from Eric's
|
|
|
|
|
* old code - mcb30
|
|
|
|
|
*/
|
|
|
|
|
#define MAX_ADDR (0xfff00000UL)
|
2005-04-09 13:12:22 +00:00
|
|
|
|
2006-05-02 15:41:21 +00:00
|
|
|
/**
|
2010-04-20 19:33:41 +01:00
|
|
|
* Relocate iPXE
|
2006-05-02 15:41:21 +00:00
|
|
|
*
|
[pcbios] Inhibit all calls to INT 15,e820 and INT 15,e801 during POST
Many BIOSes do not construct the full system memory map until after
calling the option ROM initialisation entry points. For several
years, we have added sanity checks and workarounds to accommodate
charming quirks such as BIOSes which report the entire 32-bit address
space (including all memory-mapped PCI BARs) as being usable RAM.
The IBM x3650 takes quirky behaviour to a new extreme. Calling either
INT 15,e820 or INT 15,e801 during POST doesn't just get you invalid
data. We could cope with invalid data. Instead, these nominally
read-only API calls manage to trash some internal BIOS state, with the
result that the system memory map is _never_ constructed. This tends
to confuse subsequent bootloaders and operating systems.
[ GRUB 0.97 fails in a particularly amusing way. Someone thought it
would be a good idea for memcpy() to check that the destination memory
region is a valid part of the system memory map; if not, then memcpy()
will sulk, fail, and return NULL. This breaks pretty much every use
of memcpy() including, for example, those inserted implicitly by gcc
to copy non-const initialisers. Debugging is _fun_ when a simple call
to printf() manages to create an infinite recursion, exhaust the
available stack space, and shut down the CPU. ]
Fix by completely inhibiting calls to INT 15,e820 and INT 15,e801
during POST.
We do now allow relocation during POST up to the maximum address
returned by INT 15,88 (which seems so far to always be safe). This
allows us to continue to have a reasonable size of external heap, even
if the PMM allocation is close to the 1MB mark.
The downside of allowing relocation during POST is that we may
overwrite PMM-allocated memory in use by other option ROMs. However,
the downside of inhibiting relocation, when combined with also
inhibiting calls to INT 15,e820 and INT 15,e801, would be that we
might have no external heap available: this would make booting an OS
impossible and could prevent some devices from even completing
initialisation.
On balance, the lesser evil is probably to allow relocation during
POST (up to the limit provided by INT 15,88). Entering iPXE during
POST is a rare operation; on the even rarer systems where doing so
happens to overwrite a PMM-allocated region, then there exists a
fairly simple workaround: if the user enters iPXE during POST and
wishes to exit iPXE, then the user must reboot. This is an acceptable
cost, given the rarity of the situation and the simplicity of the
workaround.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2013-03-11 00:50:52 +00:00
|
|
|
* @v ebp Maximum address to use for relocation
|
|
|
|
|
* @ret esi Current physical address
|
|
|
|
|
* @ret edi New physical address
|
|
|
|
|
* @ret ecx Length to copy
|
2006-05-02 15:41:21 +00:00
|
|
|
*
|
2010-04-20 19:33:41 +01:00
|
|
|
* This finds a suitable location for iPXE near the top of 32-bit
|
2006-05-02 15:41:21 +00:00
|
|
|
* address space, and returns the physical address of the new location
|
|
|
|
|
* to the prefix in %edi.
|
|
|
|
|
*/
|
2008-11-18 16:18:32 -08:00
|
|
|
__asmcall void relocate ( struct i386_all_regs *ix86 ) {
|
2006-05-25 00:04:13 +00:00
|
|
|
struct memory_map memmap;
|
2011-11-11 23:20:28 +00:00
|
|
|
unsigned long start, end, size, padded_size, max;
|
2006-05-25 00:04:13 +00:00
|
|
|
unsigned long new_start, new_end;
|
2005-03-08 18:53:11 +00:00
|
|
|
unsigned i;
|
2005-04-09 13:12:22 +00:00
|
|
|
|
2006-05-25 00:04:13 +00:00
|
|
|
/* Get memory map and current location */
|
|
|
|
|
get_memmap ( &memmap );
|
2008-10-09 22:22:01 +01:00
|
|
|
start = virt_to_phys ( _textdata );
|
|
|
|
|
end = virt_to_phys ( _etextdata );
|
2006-05-25 00:04:13 +00:00
|
|
|
size = ( end - start );
|
|
|
|
|
padded_size = ( size + max_align - 1 );
|
|
|
|
|
|
|
|
|
|
DBG ( "Relocate: currently at [%lx,%lx)\n"
|
|
|
|
|
"...need %lx bytes for %d-byte alignment\n",
|
|
|
|
|
start, end, padded_size, max_align );
|
|
|
|
|
|
2011-11-11 23:20:28 +00:00
|
|
|
/* Determine maximum usable address */
|
|
|
|
|
max = MAX_ADDR;
|
[pcbios] Inhibit all calls to INT 15,e820 and INT 15,e801 during POST
Many BIOSes do not construct the full system memory map until after
calling the option ROM initialisation entry points. For several
years, we have added sanity checks and workarounds to accommodate
charming quirks such as BIOSes which report the entire 32-bit address
space (including all memory-mapped PCI BARs) as being usable RAM.
The IBM x3650 takes quirky behaviour to a new extreme. Calling either
INT 15,e820 or INT 15,e801 during POST doesn't just get you invalid
data. We could cope with invalid data. Instead, these nominally
read-only API calls manage to trash some internal BIOS state, with the
result that the system memory map is _never_ constructed. This tends
to confuse subsequent bootloaders and operating systems.
[ GRUB 0.97 fails in a particularly amusing way. Someone thought it
would be a good idea for memcpy() to check that the destination memory
region is a valid part of the system memory map; if not, then memcpy()
will sulk, fail, and return NULL. This breaks pretty much every use
of memcpy() including, for example, those inserted implicitly by gcc
to copy non-const initialisers. Debugging is _fun_ when a simple call
to printf() manages to create an infinite recursion, exhaust the
available stack space, and shut down the CPU. ]
Fix by completely inhibiting calls to INT 15,e820 and INT 15,e801
during POST.
We do now allow relocation during POST up to the maximum address
returned by INT 15,88 (which seems so far to always be safe). This
allows us to continue to have a reasonable size of external heap, even
if the PMM allocation is close to the 1MB mark.
The downside of allowing relocation during POST is that we may
overwrite PMM-allocated memory in use by other option ROMs. However,
the downside of inhibiting relocation, when combined with also
inhibiting calls to INT 15,e820 and INT 15,e801, would be that we
might have no external heap available: this would make booting an OS
impossible and could prevent some devices from even completing
initialisation.
On balance, the lesser evil is probably to allow relocation during
POST (up to the limit provided by INT 15,88). Entering iPXE during
POST is a rare operation; on the even rarer systems where doing so
happens to overwrite a PMM-allocated region, then there exists a
fairly simple workaround: if the user enters iPXE during POST and
wishes to exit iPXE, then the user must reboot. This is an acceptable
cost, given the rarity of the situation and the simplicity of the
workaround.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2013-03-11 00:50:52 +00:00
|
|
|
if ( ix86->regs.ebp < max ) {
|
2011-11-11 23:20:28 +00:00
|
|
|
max = ix86->regs.ebp;
|
|
|
|
|
DBG ( "Limiting relocation to [0,%lx)\n", max );
|
|
|
|
|
}
|
|
|
|
|
|
2005-03-08 18:53:11 +00:00
|
|
|
/* Walk through the memory map and find the highest address
|
2010-04-20 19:33:41 +01:00
|
|
|
* below 4GB that iPXE will fit into.
|
2005-03-08 18:53:11 +00:00
|
|
|
*/
|
2006-05-25 00:04:13 +00:00
|
|
|
new_end = end;
|
|
|
|
|
for ( i = 0 ; i < memmap.count ; i++ ) {
|
|
|
|
|
struct memory_region *region = &memmap.regions[i];
|
2005-03-08 18:53:11 +00:00
|
|
|
unsigned long r_start, r_end;
|
2005-04-09 13:12:22 +00:00
|
|
|
|
2006-05-25 00:04:13 +00:00
|
|
|
DBG ( "Considering [%llx,%llx)\n", region->start, region->end);
|
2005-04-09 13:12:22 +00:00
|
|
|
|
2011-11-11 23:20:28 +00:00
|
|
|
/* Truncate block to maximum address. This will be
|
|
|
|
|
* less than 4GB, which means that we can get away
|
|
|
|
|
* with using just 32-bit arithmetic after this stage.
|
2005-04-09 13:12:22 +00:00
|
|
|
*/
|
2011-11-11 23:20:28 +00:00
|
|
|
if ( region->start > max ) {
|
|
|
|
|
DBG ( "...starts after max=%lx\n", max );
|
2005-03-08 18:53:11 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
2006-05-25 00:04:13 +00:00
|
|
|
r_start = region->start;
|
2011-11-11 23:20:28 +00:00
|
|
|
if ( region->end > max ) {
|
|
|
|
|
DBG ( "...end truncated to max=%lx\n", max );
|
|
|
|
|
r_end = max;
|
2005-04-09 13:12:22 +00:00
|
|
|
} else {
|
2006-05-25 00:04:13 +00:00
|
|
|
r_end = region->end;
|
2005-03-08 18:53:11 +00:00
|
|
|
}
|
2006-05-25 00:04:13 +00:00
|
|
|
DBG ( "...usable portion is [%lx,%lx)\n", r_start, r_end );
|
2005-04-09 13:12:22 +00:00
|
|
|
|
2005-03-08 18:53:11 +00:00
|
|
|
/* If we have rounded down r_end below r_ start, skip
|
|
|
|
|
* this block.
|
|
|
|
|
*/
|
|
|
|
|
if ( r_end < r_start ) {
|
2005-04-09 13:12:22 +00:00
|
|
|
DBG ( "...truncated to negative size\n" );
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-20 19:33:41 +01:00
|
|
|
/* Check that there is enough space to fit in iPXE */
|
2006-05-25 00:04:13 +00:00
|
|
|
if ( ( r_end - r_start ) < size ) {
|
|
|
|
|
DBG ( "...too small (need %lx bytes)\n", size );
|
2005-03-08 18:53:11 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
2005-04-09 13:12:22 +00:00
|
|
|
|
2010-04-20 19:33:41 +01:00
|
|
|
/* If the start address of the iPXE we would
|
2005-04-09 13:12:22 +00:00
|
|
|
* place in this block is higher than the end address
|
|
|
|
|
* of the current highest block, use this block.
|
|
|
|
|
*
|
|
|
|
|
* Note that this avoids overlaps with the current
|
2010-04-20 19:33:41 +01:00
|
|
|
* iPXE, as well as choosing the highest of all viable
|
|
|
|
|
* blocks.
|
2005-04-09 13:12:22 +00:00
|
|
|
*/
|
2006-05-25 00:04:13 +00:00
|
|
|
if ( ( r_end - size ) > new_end ) {
|
|
|
|
|
new_end = r_end;
|
2005-04-09 13:12:22 +00:00
|
|
|
DBG ( "...new best block found.\n" );
|
2005-03-08 18:53:11 +00:00
|
|
|
}
|
|
|
|
|
}
|
2005-04-09 13:12:22 +00:00
|
|
|
|
2010-04-20 19:33:41 +01:00
|
|
|
/* Calculate new location of iPXE, and align it to the
|
2005-04-09 13:12:22 +00:00
|
|
|
* required alignemnt.
|
|
|
|
|
*/
|
2006-05-25 00:04:13 +00:00
|
|
|
new_start = new_end - padded_size;
|
|
|
|
|
new_start += ( start - new_start ) & ( max_align - 1 );
|
|
|
|
|
new_end = new_start + size;
|
2005-04-09 13:12:22 +00:00
|
|
|
|
2006-05-25 00:04:13 +00:00
|
|
|
DBG ( "Relocating from [%lx,%lx) to [%lx,%lx)\n",
|
|
|
|
|
start, end, new_start, new_end );
|
2006-05-02 15:41:21 +00:00
|
|
|
|
2006-05-25 00:04:13 +00:00
|
|
|
/* Let prefix know what to copy */
|
|
|
|
|
ix86->regs.esi = start;
|
|
|
|
|
ix86->regs.edi = new_start;
|
|
|
|
|
ix86->regs.ecx = size;
|
2005-03-08 18:53:11 +00:00
|
|
|
}
|