mirror of
https://github.com/ipxe/ipxe
synced 2025-12-12 14:32:49 +03:00
Moved relocate.c to arch-specific dir, since it involves arch-specific
logic (e.g. avoiding memory regions with A20=0).
This commit is contained in:
104
src/arch/i386/core/relocate.c
Normal file
104
src/arch/i386/core/relocate.c
Normal file
@@ -0,0 +1,104 @@
|
||||
#ifndef NORELOCATE
|
||||
|
||||
#include "etherboot.h"
|
||||
#include "memsizes.h"
|
||||
|
||||
/* by Eric Biederman */
|
||||
|
||||
/* On some platforms etherboot is compiled as a shared library, and we use
|
||||
* the ELF pic support to make it relocateable. This works very nicely
|
||||
* for code, but since no one has implemented PIC data yet pointer
|
||||
* values in variables are a a problem. Global variables are a
|
||||
* pain but the return addresses on the stack are the worst. On these
|
||||
* platforms relocate_to will restart etherboot, to ensure the stack
|
||||
* is reinitialize and hopefully get the global variables
|
||||
* appropriately reinitialized as well.
|
||||
*
|
||||
*/
|
||||
|
||||
void relocate(void)
|
||||
{
|
||||
unsigned long addr, eaddr, size;
|
||||
unsigned i;
|
||||
/* Walk through the memory map and find the highest address
|
||||
* below 4GB that etherboot will fit into. Ensure etherboot
|
||||
* lies entirely within a range with A20=0. This means that
|
||||
* even if something screws up the state of the A20 line, the
|
||||
* etherboot code is still visible and we have a chance to
|
||||
* diagnose the problem.
|
||||
*/
|
||||
/* First find the size of etherboot */
|
||||
addr = virt_to_phys(_text);
|
||||
eaddr = virt_to_phys(_end);
|
||||
size = (eaddr - addr + 0xf) & ~0xf;
|
||||
|
||||
/* If the current etherboot is beyond MAX_ADDR pretend it is
|
||||
* at the lowest possible address.
|
||||
*/
|
||||
if (eaddr > MAX_ADDR) {
|
||||
eaddr = 0;
|
||||
}
|
||||
|
||||
for(i = 0; i < meminfo.map_count; i++) {
|
||||
unsigned long r_start, r_end;
|
||||
if (meminfo.map[i].type != E820_RAM) {
|
||||
continue;
|
||||
}
|
||||
if (meminfo.map[i].addr > MAX_ADDR) {
|
||||
continue;
|
||||
}
|
||||
if (meminfo.map[i].size > MAX_ADDR) {
|
||||
continue;
|
||||
}
|
||||
r_start = meminfo.map[i].addr;
|
||||
r_end = r_start + meminfo.map[i].size;
|
||||
/* Make the addresses 16 byte (128 bit) aligned */
|
||||
r_start = (r_start + 15) & ~15;
|
||||
r_end = r_end & ~15;
|
||||
if (r_end < r_start) {
|
||||
r_end = MAX_ADDR;
|
||||
}
|
||||
if (r_end < size) {
|
||||
/* Avoid overflow weirdness when r_end - size < 0 */
|
||||
continue;
|
||||
}
|
||||
/* Shrink the range down to use only even megabytes
|
||||
* (i.e. A20=0).
|
||||
*/
|
||||
if ( r_end & 0x100000 ) {
|
||||
/* If r_end is in an odd megabyte, round down
|
||||
* r_end to the top of the next even megabyte.
|
||||
*/
|
||||
r_end = r_end & ~0xfffff;
|
||||
} else if ( ( r_end - size ) & 0x100000 ) {
|
||||
/* If r_end is in an even megabyte, but the
|
||||
* start of Etherboot would be in an odd
|
||||
* megabyte, round down to the top of the next
|
||||
* even megabyte.
|
||||
*/
|
||||
r_end = ( r_end - 0x100000 ) & ~0xfffff;
|
||||
}
|
||||
/* If we have rounded down r_end below r_ start, skip
|
||||
* this block.
|
||||
*/
|
||||
if ( r_end < r_start ) {
|
||||
continue;
|
||||
}
|
||||
if (eaddr < r_end - size) {
|
||||
addr = r_end - size;
|
||||
eaddr = r_end;
|
||||
}
|
||||
}
|
||||
if (addr != virt_to_phys(_text)) {
|
||||
unsigned long old_addr = virt_to_phys(_text);
|
||||
printf("Relocating _text from: [%lx,%lx) to [%lx,%lx)\n",
|
||||
old_addr, virt_to_phys(_end),
|
||||
addr, eaddr);
|
||||
/* arch_relocate_to ( addr ) */
|
||||
cleanup();
|
||||
relocate_to(addr);
|
||||
/* arch_relocated_from ( addr ) */
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* NORELOCATE */
|
||||
Reference in New Issue
Block a user