diff options
| author | Michael Brown | 2005-04-09 12:49:26 +0200 |
|---|---|---|
| committer | Michael Brown | 2005-04-09 12:49:26 +0200 |
| commit | a4920c16bd1bff5d0edfbc2b88dcabc5aa546489 (patch) | |
| tree | bfb92ab470ae052729dd12dcb48bd9aeb49ed138 /src/arch | |
| parent | Merged mcb30-realmode-redesign back to HEAD (diff) | |
| download | ipxe-a4920c16bd1bff5d0edfbc2b88dcabc5aa546489.tar.gz ipxe-a4920c16bd1bff5d0edfbc2b88dcabc5aa546489.tar.xz ipxe-a4920c16bd1bff5d0edfbc2b88dcabc5aa546489.zip | |
Moved relocate.c to arch-specific dir, since it involves arch-specific
logic (e.g. avoiding memory regions with A20=0).
Diffstat (limited to 'src/arch')
| -rw-r--r-- | src/arch/i386/core/relocate.c | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/src/arch/i386/core/relocate.c b/src/arch/i386/core/relocate.c new file mode 100644 index 000000000..436432e5c --- /dev/null +++ b/src/arch/i386/core/relocate.c @@ -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 */ |
